Skip to content

Commit bad2894

Browse files
committed
fixed file uploading feature
1 parent 5e914c3 commit bad2894

2 files changed

Lines changed: 38 additions & 39 deletions

File tree

modules/wljs-inputs/src/Kernel.wl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,7 @@ InputFile[opts: OptionsPattern[] ] := With[{id = OptionValue["Event"], internal
356356
"Transaction" -> (EventFire[id, "Transaction", #]&), (* forward to the main event *)
357357
"File" -> (EventFire[id, "File", #]&), (* forward to the main event *)
358358

359-
"Chunk" -> Function[payload, With[{hash = StringJoin[payload["Name"], payload["Transaction"] ], chunk = payload["Chunk"] },
359+
"Chunk" -> Function[payload, With[{hash = StringJoin[payload["Name"], "|", payload["Transaction"] ], chunk = payload["Chunk"] },
360360

361361
If[!KeyExistsQ[filechunks, hash ], filechunks[hash] = <||>];
362362
filechunks[hash] = Join[filechunks[hash], <|chunk -> payload["Data"]|>];

modules/wljs-inputs/templates/Drop.wlx

Lines changed: 37 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
Component[OptionsPattern[]] := With[{Class = OptionValue["Class"], Event = OptionValue["Event"], Label = OptionValue["Label"], Descr = OptionValue["Description"]},
22
{
33
<div class="flex items-center sm-controls {Class}">
4-
<label id="label-#instanceId" for="#instanceId" class="relative transition-all cursor-default rounded-md 0 pl-3 pr-2 text-left text-gray-500 ring-1 ring-inset ring-gray-400 focus:outline-none focus:ring-1 focus:ring-indigo-600 sm:text-xs sm:leading-6 bg-gray-200 hover:bg-gray-100">
5-
<div id="bar-#instanceId" class="absolute bg-gray-400/30 top-0 left-0 w-0 h-full z-0 rounded-md transition-all" style="width:0%"></div>
4+
<label id="label-#instanceId" for="#instanceId" class="relative transition-all cursor-default rounded-md 0 pl-3 pr-2 text-left text-gray-500 ring-1 ring-inset ring-gray-400 focus:outline-none focus:ring-1 focus:ring-indigo-600 sm:text-xs sm:leading-6 bg-gray-200 hover:bg-gray-100" style="background:white">
5+
<div id="bar-#instanceId" class="absolute top-0 left-0 w-0 h-full z-0 rounded-md transition-all" style="width:0%"></div>
66
<div class="flex flex-col items-center justify-center py-2">
77
<p class="text-sm text-gray-500 pr-1 flex gap-x-1 items-center z-50">
88
<svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" >
@@ -26,19 +26,9 @@ Component[OptionsPattern[]] := With[{Class = OptionValue["Class"], Event = Optio
2626

2727
let dragged = false;
2828

29-
const splitStringIntoChunks0 = (str, chunkSize) => {
30-
if (!str || chunkSize <= 0) return [];
31-
32-
const chunks = [];
33-
for (let i = 0; i < str.length; i += chunkSize) {
34-
chunks.push(str.slice(i, Math.min(i + chunkSize, str.length)));
35-
}
36-
return chunks;
37-
}
38-
3929
const transferFiles = (list) => {
4030
if (list.length == 0) return;
41-
const id = new Date().valueOf();
31+
const id = crypto.randomUUID();
4232
let count = 0;
4333

4434
const progress = (num) => {
@@ -65,41 +55,50 @@ Component[OptionsPattern[]] := With[{Class = OptionValue["Class"], Event = Optio
6555
};
6656

6757
progress(0);
68-
server.kernel.emitt('<Event/>', `<|"Id" -> "${id}", "Length" -> ${list.length}|>`, 'Transaction');
58+
server.kernel.io.fire('<Event/>', {Id: id, Length: list.length}, 'Transaction');
6959

7060
for (const file of list) {
71-
readFile(file, (name, result) => {
72-
if (result.length > 5 * 1024 * 1024) {
73-
const chunks = splitStringIntoChunks0(result, 5 * 1024 * 1024);
74-
chunks.forEach((chunk, index) => {
75-
server.kernel.emitt('<Event/>', `<|"Transaction" -> "${id}", "Chunk"->${index+1}, "Chunks"->${chunks.length}, "Name" -> "${name}", "Data" -> "${chunk}"|>`, 'Chunk');
76-
});
61+
readFileChunked(file, id, (name, chunkData, chunkIndex, totalChunks) => {
62+
if (totalChunks === 1) {
63+
server.kernel.io.fire('<Event/>', {Transaction: id, Name: name, Data: chunkData}, 'File');
7764
} else {
78-
server.kernel.emitt('<Event/>', `<|"Transaction" -> "${id}", "Name" -> "${name}", "Data" -> "${result}"|>`, 'File');
65+
server.kernel.io.fire('<Event/>', {Transaction: id, Chunk: chunkIndex + 1, Chunks: totalChunks, Name: name, Data: chunkData}, 'Chunk');
7966
}
67+
}, () => {
8068
count++;
8169
progress(count);
82-
})
70+
});
8371
}
8472

8573
}
8674

87-
function readFile(file, cbk) {
88-
const reader = new FileReader();
89-
reader.addEventListener('load', (event) => {
90-
let compressedData = base64ArrayBuffer(event.target.result);
91-
console.log(compressedData);
92-
cbk(file.name, compressedData);
93-
});
94-
95-
reader.addEventListener('progress', (event) => {
96-
if (event.loaded && event.total) {
97-
const percent = (event.loaded / event.total) * 100;
98-
console.log(percent);
99-
}
100-
});
101-
102-
reader.readAsArrayBuffer(file);
75+
function arrayBufferToBase64(buffer) {
76+
let binary = '';
77+
const bytes = new Uint8Array(buffer);
78+
for (let i = 0; i < bytes.byteLength; i++) binary += String.fromCharCode(bytes[i]);
79+
return btoa(binary);
80+
}
81+
82+
function readFileChunked(file, id, onChunk, onDone) {
83+
const CHUNK_BYTES = 5 * 1024 * 1024;
84+
const totalChunks = Math.ceil(file.size / CHUNK_BYTES) || 1;
85+
let chunkIndex = 0;
86+
87+
const readNext = () => {
88+
if (chunkIndex >= totalChunks) { onDone(); return; }
89+
const start = chunkIndex * CHUNK_BYTES;
90+
const slice = file.slice(start, start + CHUNK_BYTES);
91+
const reader = new FileReader();
92+
reader.addEventListener('load', (event) => {
93+
const encoded = arrayBufferToBase64(event.target.result);
94+
onChunk(file.name, encoded, chunkIndex, totalChunks);
95+
chunkIndex++;
96+
readNext();
97+
});
98+
reader.readAsArrayBuffer(slice);
99+
};
100+
101+
readNext();
103102
}
104103

105104
element.addEventListener('change', (event) => {

0 commit comments

Comments
 (0)