Upload files via form chunks

This commit is contained in:
Mikei386
2026-06-23 21:53:49 +02:00
parent c661cbaf46
commit 76fc7e7251
6 changed files with 82 additions and 26 deletions
+41 -23
View File
@@ -904,25 +904,37 @@ async function uploadFileList(win, files, targetPath) {
const uploadItems = normalizeUploadItems(files);
for (const [index, item] of uploadItems.entries()) {
recordDebug('upload.file.request', { path: item.path, size: item.file.size, index: index + 1, total: uploadItems.length });
const params = new URLSearchParams({
path: targetPath,
relativePath: item.path
});
const token = getCsrfToken();
if (token) {
params.set('csrf_token', token);
recordDebug('csrf.attached', { length: token.length });
} else {
recordDebug('csrf.missing', {});
}
const response = await fetch(apiUrl(`upload.php?${params.toString()}`), {
await uploadFileChunked(targetPath, item.file, item.path);
recordDebug('upload.file.done', { path: item.path, index: index + 1, total: uploadItems.length });
}
await loadExplorer(win, targetPath);
}
async function uploadFileChunked(targetPath, file, relativePath) {
const chunkSize = 256 * 1024;
const totalChunks = Math.max(1, Math.ceil(file.size / chunkSize));
let finalPayload = null;
for (let chunkIndex = 0; chunkIndex < totalChunks; chunkIndex += 1) {
const start = chunkIndex * chunkSize;
const chunk = file.slice(start, Math.min(file.size, start + chunkSize));
const data = arrayBufferToBase64(await chunk.arrayBuffer());
const body = new URLSearchParams();
body.set('uploadMode', 'chunk');
body.set('relativePath', relativePath);
body.set('chunkIndex', String(chunkIndex));
body.set('totalChunks', String(totalChunks));
body.set('data', data);
appendCsrf(body);
const response = await fetch(apiUrl(`upload.php?path=${encodeURIComponent(targetPath)}`), {
method: 'POST',
headers: { 'Content-Type': 'application/octet-stream' },
body: item.file
headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
body
});
const text = await response.text();
recordDebug('upload.file.response', {
path: item.path,
recordDebug('upload.chunk.response', {
path: relativePath,
chunk: chunkIndex + 1,
totalChunks,
status: response.status,
ok: response.ok,
contentType: response.headers.get('content-type'),
@@ -931,18 +943,24 @@ async function uploadFileList(win, files, targetPath) {
if (!response.ok) {
throw new Error(text || `${response.status} ${response.statusText}`);
}
let payload;
try {
payload = JSON.parse(text);
finalPayload = JSON.parse(text);
} catch (error) {
throw new Error(`Upload lieferte keine JSON-Antwort: ${text.slice(0, 200) || response.headers.get('content-type') || 'leer'}`);
}
if (!payload.uploaded?.length) {
throw new Error('Upload wurde vom Server nicht bestätigt.');
}
recordDebug('upload.file.done', { path: item.path, index: index + 1, total: uploadItems.length });
}
await loadExplorer(win, targetPath);
if (!finalPayload?.uploaded?.length) {
throw new Error('Upload wurde vom Server nicht bestätigt.');
}
}
function arrayBufferToBase64(buffer) {
const bytes = new Uint8Array(buffer);
let binary = '';
for (let index = 0; index < bytes.length; index += 0x8000) {
binary += String.fromCharCode(...bytes.subarray(index, index + 0x8000));
}
return btoa(binary);
}
function normalizeUploadItems(files) {