File are not uploading fully. Only one chunk size of data is transfer from frontend to backend. The network tab showing status 204 and 206. Status 206 is what i am setting to show that the file is not uploaded fully so that in the next req the file chunk get appended to the same file in a loop till whole file is transferred. I am storing the information about how many bytes uploaded to the server in the localStorage so that when the user gets disconnected due to any reason with the server, i can retreive the information about how many bytes already uploaded so that the file upload take place after those bytes and implement resumable uploading.
Frontend code in ReactJs
import React, { useState } from "react";
function ResumableFileUploader() {
const [file, setFile] = useState();
const [output, setOutput] = useState("");
const [uploading, setUploading] = useState(false);
const handleFile = (e) => {
const file = e.target.files[0];
console.log("file", file);
setFile(file);
};
const handleUpload = async () => {
if (!file) {
return;
}
setUploading(true);
const fileName = file.name;
console.log("fileName l-190: ", fileName);
console.log("filesize l-190: ", file.size);
const storedProgress = localStorage.getItem(fileName);
let offset = 0;
if (storedProgress) {
offset = parseInt(storedProgress);
}
const fileReader = new FileReader();
fileReader.onload = async (event) => {
const CHUNK_SIZE = 1 * 1024;
const chunkCount = Math.ceil(file.size / CHUNK_SIZE);
console.log(`chunk count ${chunkCount}`);
console.log("Read successfully");
for (let chunkId = offset; chunkId < chunkCount; chunkId++) {
const start = chunkId * CHUNK_SIZE;
const end = Math.min(start + CHUNK_SIZE, file.size);
const chunk = file.slice(start, end);
const res = await fetch("http://localhost:8080/upload", {
method: "POST",
headers: {
"content-type": "application/octet-stream",
"content-length": chunk,
"file-name": fileName,
"file-offset": start,
},
body: chunk,
});
if (res.status === 200) {
console.log("if called");
const progress = Math.round(((chunkId + 1) * 100) / chunkCount, 0);
setOutput(progress + "%");
localStorage.setItem(fileName, start + chunk.size);
} else if (res.status === 206) {
console.log("else called");
const rangeHeader = res.headers.get("content-range");
console.log("rangeHeader: ", rangeHeader);
const [, currentOffset, totalSize] = rangeHeader.match(
/bytes \d+-(\d+)\/(\d+)/
);
const progress = Math.round(
((parseInt(currentOffset) + chunk.size) * 100) / file.size,
0
);
setOutput(progress + "%");
localStorage.setItem(fileName, parseInt(currentOffset) + chunk.size);
} else {
setOutput("Upload failed!");
localStorage.removeItem(fileName);
break;
}
}
if (offset === chunkCount) {
setOutput("Upload successful!");
localStorage.removeItem(fileName);
}
setUploading(false);
};
fileReader.readAsArrayBuffer(file);
};
return (
<div>
<h1>Resumable uploader</h1>
<input type="file" onChange={handleFile} />
<button onClick={handleUpload} disabled={uploading}>
{uploading ? "Uploading..." : "Upload"}
</button>
{output && <div>Progress: {output}</div>}
</div>
);
}
export default ResumableFileUploader;
backend code in ExpressJS
const fs = require("fs");
const cors = require("cors");
const express = require("express");
const app = express();
const port = 8080;
app.use(cors());
app.get("/", (req, res) => {
res.end(fs.readFileSync("index.html"));
});
app.post("/upload", (req, res) => {
console.log("called")
const fileName = req.headers["file-name"];
console.log("fileName ", fileName)
const fileOffset = Number(req.headers["file-offset"]) || 0;
console.log(`fileoffset ${fileOffset}`)
const fileSize = Number(req.headers["content-length"]);
console.log(`fileSize ${fileSize}`)
const fileStream = fs.createWriteStream(`./video/${fileName}`, {
flags: fileOffset ? "r+" : "w",
start: fileOffset,
});
req.on("data", (chunk) => {
fileStream.write(chunk);
console.log("file byte written", fileStream.bytesWritten)
// console.log(`chunk ${chunk}`)
});
req.on("end", () => {
fileStream.end();
console.log(`received ${fileSize} bytes for ${fileName}`);
res.setHeader("Content-Length", 0);
if (fileStream.bytesWritten < fileSize) {
res.status(206);
res.setHeader(
"Range",
`bytes=${fileStream.bytesWritten}-${fileSize - 1}`
);
res.end();
} else {
res.end("uploaded!");
}
});
});
const server = app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});