Fix WebSocket upload - reduce chunk size and add stability

- Reduce chunk size from 16KB to 4KB for ESP32 stability
- Add 50ms delay after READY before sending chunks
- Move chunk sending inline for proper error handling
- More aggressive backpressure (wait when buffer > 8KB)
- Check WebSocket state before each send
- Better error handling and close event logging
- Set binaryType = 'arraybuffer' explicitly
This commit is contained in:
Claude 2026-01-13 01:16:42 +00:00
parent 528102f63c
commit 6ff7796b3a
No known key found for this signature in database

View File

@ -818,7 +818,7 @@
let failedUploadsGlobal = [];
let wsConnection = null;
const WS_PORT = 81;
const WS_CHUNK_SIZE = 16384; // 16KB chunks for WebSocket - larger = faster
const WS_CHUNK_SIZE = 4096; // 4KB chunks - smaller for ESP32 stability
// Get WebSocket URL based on current page location
function getWsUrl() {
@ -831,6 +831,9 @@ function uploadFileWebSocket(file, onProgress, onComplete, onError) {
return new Promise((resolve, reject) => {
const ws = new WebSocket(getWsUrl());
let uploadStarted = false;
let sendingChunks = false;
ws.binaryType = 'arraybuffer';
ws.onopen = function() {
console.log('[WS] Connected, starting upload:', file.name);
@ -838,15 +841,53 @@ function uploadFileWebSocket(file, onProgress, onComplete, onError) {
ws.send(`START:${file.name}:${file.size}:${currentPath}`);
};
ws.onmessage = function(event) {
ws.onmessage = async function(event) {
const msg = event.data;
console.log('[WS] Message:', msg);
if (msg === 'READY') {
uploadStarted = true;
// Start sending binary data in chunks
sendFileChunks(ws, file, onProgress);
sendingChunks = true;
// Small delay to let connection stabilize
await new Promise(r => setTimeout(r, 50));
try {
// Send file in chunks
const totalSize = file.size;
let offset = 0;
while (offset < totalSize && ws.readyState === WebSocket.OPEN) {
const chunkSize = Math.min(WS_CHUNK_SIZE, totalSize - offset);
const chunk = file.slice(offset, offset + chunkSize);
const buffer = await chunk.arrayBuffer();
// Wait for buffer to clear - more aggressive backpressure
while (ws.bufferedAmount > WS_CHUNK_SIZE * 2 && ws.readyState === WebSocket.OPEN) {
await new Promise(r => setTimeout(r, 5));
}
if (ws.readyState !== WebSocket.OPEN) {
throw new Error('WebSocket closed during upload');
}
ws.send(buffer);
offset += chunkSize;
// Update local progress
if (onProgress) onProgress(offset, totalSize);
}
sendingChunks = false;
console.log('[WS] All chunks sent, waiting for DONE');
} catch (err) {
console.error('[WS] Error sending chunks:', err);
sendingChunks = false;
ws.close();
reject(err);
}
} else if (msg.startsWith('PROGRESS:')) {
// Server confirmed progress - we can use this for accurate tracking
const parts = msg.split(':');
const received = parseInt(parts[1]);
const total = parseInt(parts[2]);
@ -866,39 +907,21 @@ function uploadFileWebSocket(file, onProgress, onComplete, onError) {
ws.onerror = function(event) {
console.error('[WS] Error:', event);
if (!uploadStarted) {
// WebSocket connection failed, reject to trigger fallback
reject(new Error('WebSocket connection failed'));
} else if (!sendingChunks) {
reject(new Error('WebSocket error during upload'));
}
};
ws.onclose = function() {
console.log('[WS] Connection closed');
ws.onclose = function(event) {
console.log('[WS] Connection closed, code:', event.code, 'reason:', event.reason);
if (sendingChunks) {
reject(new Error('WebSocket closed unexpectedly'));
}
};
});
}
// Send file in chunks via WebSocket
async function sendFileChunks(ws, file, onProgress) {
const totalSize = file.size;
let offset = 0;
while (offset < totalSize) {
const chunk = file.slice(offset, offset + WS_CHUNK_SIZE);
const buffer = await chunk.arrayBuffer();
// Wait for buffer to clear if needed
while (ws.bufferedAmount > WS_CHUNK_SIZE * 4) {
await new Promise(r => setTimeout(r, 10));
}
ws.send(buffer);
offset += chunk.size;
// Update local progress (server will confirm)
if (onProgress) onProgress(offset, totalSize);
}
}
// Upload file via HTTP (fallback method)
function uploadFileHTTP(file, onProgress, onComplete, onError) {
return new Promise((resolve, reject) => {