0

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}`);
});

0 Answers0