Skip to content

Commit 4eb36b0

Browse files
fix(fs): writeFile with ReadableStream throws (#2907)
1 parent b8056f4 commit 4eb36b0

File tree

4 files changed

+104
-97
lines changed

4 files changed

+104
-97
lines changed

.changes/fs-write-stream.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
fs: patch
3+
fs-js: patch
4+
---
5+
6+
Fixed calling `writeFile` with `data: ReadableStream` throws `Invalid argument`

examples/api/src/views/FileSystem.svelte

Lines changed: 91 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -1,194 +1,190 @@
11
<script>
2-
import * as fs from "@tauri-apps/plugin-fs";
3-
import { convertFileSrc } from "@tauri-apps/api/core";
4-
import { arrayBufferToBase64 } from "../lib/utils";
5-
import { onDestroy } from "svelte";
2+
import * as fs from '@tauri-apps/plugin-fs'
3+
import { convertFileSrc } from '@tauri-apps/api/core'
4+
import { arrayBufferToBase64 } from '../lib/utils'
5+
import { onDestroy } from 'svelte'
66
7-
export let onMessage;
8-
export let insecureRenderHtml;
7+
const { onMessage, insecureRenderHtml } = $props()
98
10-
let path = "";
11-
let img;
9+
let path = $state('')
10+
let img
1211
/** @type {fs.FileHandle} */
13-
let file;
14-
let renameTo;
15-
let watchPath = "";
16-
let watchDebounceDelay = "0";
17-
let watchRecursive = false;
18-
let unwatchFn;
19-
let unwatchPath = "";
20-
21-
function getDir() {
22-
const dirSelect = document.getElementById("dir");
23-
return dirSelect.value ? parseInt(dir.value) : null;
24-
}
25-
26-
const DirOptions = Object.keys(fs.BaseDirectory)
27-
.filter((key) => isNaN(parseInt(key)))
28-
.map((dir) => [dir, fs.BaseDirectory[dir]]);
12+
let file = $state()
13+
let renameTo = $state()
14+
let watchPath = $state('')
15+
let watchDebounceDelay = $state(0)
16+
let watchRecursive = $state(false)
17+
/** @type {fs.BaseDirectory | undefined} */
18+
let baseDir = $state()
19+
let unwatchFn
20+
let unwatchPath = ''
21+
22+
const dirOptions = Object.keys(fs.BaseDirectory).filter((key) =>
23+
isNaN(parseInt(key))
24+
)
2925
3026
function open() {
3127
fs.open(path, {
32-
baseDir: getDir(),
28+
baseDir,
3329
read: true,
3430
write: true,
35-
create: true,
31+
create: true
3632
})
3733
.then((f) => {
38-
file = f;
39-
onMessage(`Opened ${path}`);
34+
file = f
35+
onMessage(`Opened ${path}`)
4036
})
41-
.catch(onMessage);
37+
.catch(onMessage)
4238
}
4339
4440
function mkdir() {
45-
fs.mkdir(path, { baseDir: getDir() })
41+
fs.mkdir(path, { baseDir })
4642
.then(() => {
47-
onMessage(`Created dir ${path}`);
43+
onMessage(`Created dir ${path}`)
4844
})
49-
.catch(onMessage);
45+
.catch(onMessage)
5046
}
5147
5248
function remove() {
53-
fs.remove(path, { baseDir: getDir() })
49+
fs.remove(path, { baseDir })
5450
.then(() => {
55-
onMessage(`Removed ${path}`);
51+
onMessage(`Removed ${path}`)
5652
})
57-
.catch(onMessage);
53+
.catch(onMessage)
5854
}
5955
6056
function rename() {
6157
fs.rename(path, renameTo, {
62-
oldPathBaseDir: getDir(),
63-
newPathBaseDir: getDir(),
58+
oldPathBaseDir,
59+
newPathBaseDir
6460
})
6561
.then(() => {
66-
onMessage(`Renamed ${path} to ${renameTo}`);
62+
onMessage(`Renamed ${path} to ${renameTo}`)
6763
})
68-
.catch(onMessage);
64+
.catch(onMessage)
6965
}
7066
7167
function truncate() {
7268
file
7369
.truncate(0)
7470
.then(() => {
75-
onMessage(`Truncated file`);
71+
onMessage(`Truncated file`)
7672
})
77-
.catch(onMessage);
73+
.catch(onMessage)
7874
}
7975
8076
function stat() {
8177
file
8278
.stat()
8379
.then((stat) => {
84-
onMessage(`File stat ${JSON.stringify(stat)}`);
80+
onMessage(`File stat ${JSON.stringify(stat)}`)
8581
})
86-
.catch(onMessage);
82+
.catch(onMessage)
8783
}
8884
8985
function read() {
9086
const opts = {
91-
baseDir: getDir(),
92-
};
87+
baseDir
88+
}
9389
fs.stat(path, opts)
9490
.then((stat) => {
95-
const isFile = stat.isFile;
91+
const isFile = stat.isFile
9692
9793
const promise = isFile
9894
? fs.readFile(path, opts)
99-
: fs.readDir(path, opts);
95+
: fs.readDir(path, opts)
10096
promise
10197
.then(function (response) {
10298
if (isFile) {
103-
if (path.includes(".png") || path.includes(".jpg")) {
99+
if (path.includes('.png') || path.includes('.jpg')) {
104100
arrayBufferToBase64(
105101
new Uint8Array(response),
106102
function (base64) {
107-
const src = "data:image/png;base64," + base64;
108-
insecureRenderHtml('<img src="' + src + '"></img>');
103+
const src = 'data:image/png;base64,' + base64
104+
insecureRenderHtml('<img src="' + src + '"></img>')
109105
}
110-
);
106+
)
111107
} else {
112-
const value = String.fromCharCode.apply(null, response);
108+
const value = String.fromCharCode.apply(null, response)
113109
insecureRenderHtml(
114110
'<textarea id="file-response"></textarea><button id="file-save">Save</button>'
115-
);
111+
)
116112
setTimeout(() => {
117-
const fileInput = document.getElementById("file-response");
118-
fileInput.value = value;
113+
const fileInput = document.getElementById('file-response')
114+
fileInput.value = value
119115
document
120-
.getElementById("file-save")
121-
.addEventListener("click", function () {
116+
.getElementById('file-save')
117+
.addEventListener('click', function () {
122118
fs.writeTextFile(path, fileInput.value, {
123-
baseDir: getDir(),
124-
}).catch(onMessage);
125-
});
126-
});
119+
baseDir
120+
}).catch(onMessage)
121+
})
122+
})
127123
}
128124
} else {
129-
onMessage(response);
125+
onMessage(response)
130126
}
131127
})
132-
.catch(onMessage);
128+
.catch(onMessage)
133129
})
134-
.catch(onMessage);
130+
.catch(onMessage)
135131
}
136132
137133
function setSrc() {
138-
img.src = convertFileSrc(path);
134+
img.src = convertFileSrc(path)
139135
}
140136
141137
function watch() {
142-
unwatch();
138+
unwatch()
143139
if (watchPath) {
144-
onMessage(`Watching ${watchPath} for changes`);
140+
onMessage(`Watching ${watchPath} for changes`)
145141
let options = {
146142
recursive: watchRecursive,
147-
delayMs: parseInt(watchDebounceDelay),
148-
};
143+
delayMs: watchDebounceDelay
144+
}
149145
if (options.delayMs === 0) {
150146
fs.watchImmediate(watchPath, onMessage, options)
151147
.then((fn) => {
152-
unwatchFn = fn;
153-
unwatchPath = watchPath;
148+
unwatchFn = fn
149+
unwatchPath = watchPath
154150
})
155-
.catch(onMessage);
151+
.catch(onMessage)
156152
} else {
157153
fs.watch(watchPath, onMessage, options)
158154
.then((fn) => {
159-
unwatchFn = fn;
160-
unwatchPath = watchPath;
155+
unwatchFn = fn
156+
unwatchPath = watchPath
161157
})
162-
.catch(onMessage);
158+
.catch(onMessage)
163159
}
164160
}
165161
}
166162
167163
function unwatch() {
168164
if (unwatchFn) {
169-
onMessage(`Stopped watching ${unwatchPath} for changes`);
170-
unwatchFn();
165+
onMessage(`Stopped watching ${unwatchPath} for changes`)
166+
unwatchFn()
171167
}
172-
unwatchFn = undefined;
173-
unwatchPath = undefined;
168+
unwatchFn = undefined
169+
unwatchPath = undefined
174170
}
175171
176172
onDestroy(() => {
177173
if (file) {
178-
file.close();
174+
file.close()
179175
}
180176
if (unwatchFn) {
181-
unwatchFn();
177+
unwatchFn()
182178
}
183179
})
184180
</script>
185181

186182
<div class="flex flex-col">
187183
<div class="flex gap-1">
188-
<select class="input" id="dir">
189-
<option value="">None</option>
190-
{#each DirOptions as dir}
191-
<option value={dir[1]}>{dir[0]}</option>
184+
<select class="input" bind:value={baseDir}>
185+
<option value={undefined} selected>None</option>
186+
{#each dirOptions as dir}
187+
<option value={fs.BaseDirectory[dir]}>{dir}</option>
192188
{/each}
193189
</select>
194190
<input
@@ -199,20 +195,20 @@
199195
</div>
200196
<br />
201197
<div>
202-
<button class="btn" on:click={open}>Open</button>
203-
<button class="btn" on:click={read}>Read</button>
204-
<button class="btn" on:click={mkdir}>Mkdir</button>
205-
<button class="btn" on:click={remove}>Remove</button>
198+
<button class="btn" onclick={open}>Open</button>
199+
<button class="btn" onclick={read}>Read</button>
200+
<button class="btn" onclick={mkdir}>Mkdir</button>
201+
<button class="btn" onclick={remove}>Remove</button>
206202
<div class="flex flex-row">
207-
<button class="btn" on:click={rename}>Rename</button>
203+
<button class="btn" onclick={rename}>Rename</button>
208204
<input class="input" bind:value={renameTo} placeholder="To" />
209205
</div>
210-
<button class="btn" type="button" on:click={setSrc}>Use as img src</button>
206+
<button class="btn" type="button" onclick={setSrc}>Use as img src</button>
211207
</div>
212208
{#if file}
213209
<div>
214-
<button class="btn" on:click={truncate}>Truncate</button>
215-
<button class="btn" on:click={stat}>Stat</button>
210+
<button class="btn" onclick={truncate}>Truncate</button>
211+
<button class="btn" onclick={stat}>Stat</button>
216212
</div>
217213
{/if}
218214

@@ -241,8 +237,8 @@
241237
</div>
242238
<br />
243239
<div>
244-
<button class="btn" on:click={watch}>Watch</button>
245-
<button class="btn" on:click={unwatch}>Unwatch</button>
240+
<button class="btn" onclick={watch}>Watch</button>
241+
<button class="btn" onclick={unwatch}>Unwatch</button>
246242
</div>
247243
</div>
248244

0 commit comments

Comments
 (0)