21xrx.com
2025-06-24 08:50:27 Tuesday
登录
文章检索 我的文章 写文章
Node.js 实现大文件上传
2023-07-11 20:29:22 深夜i     109     0
Node js 大文件上传 流式传输 分片上传 断点续传

Node.js是一款基于Chrome V8引擎的JavaScript运行环境。它可以在服务端执行JavaScript代码,为我们提供了一种快速、高效的服务器端开发方式。在网站或应用的开发中,文件上传功能是必不可少的。然而,当要上传的文件很大时,就会出现一些问题,如上传速度慢、内存占用过大等。针对这些问题,Node.js可以通过一些优化来实现大文件上传。

在Node.js中,实现大文件上传主要有两种方式:一种是通过流式传输,另一种是通过分片上传。对于大文件上传,流式传输是一种较好的选择。我们可以使用Node.js内置的HTTP模块来处理上传请求。通过对请求对象request进行监听并将其转换为可写流,我们可以将上传的文件分成多个小块,然后逐个小块地传输,以避免出现内存占用过大的情况。

代码如下所示:

const http = require('http');
const fs = require('fs');
const server = http.createServer((req, res) => {
 const isFormData = req.headers['content-type'].includes('multipart/form-data');
 if (req.method === 'POST' && isFormData) {
  const chunks = [];
  req.on('data', (chunk) => {
   chunks.push(chunk);
  });
  req.on('end', () => {
   const buffer = Buffer.concat(chunks);
   fs.writeFile('upload-file.txt', buffer, (err) => {
    if (err) {
     console.error(err);
     res.writeHead(500, { 'Content-Type': 'text/plain' });
     res.end('Server Error');
    } else {
     res.writeHead(200, { 'Content-Type': 'text/plain' });
     res.end('Upload Success');
    }
   });
  });
 } else {
  res.writeHead(400, { 'Content-Type': 'text/plain' });
  res.end('Bad Request');
 }
});
server.listen(3000, () => {
 console.log('Server is running on http://localhost:3000');
});

另外,针对大文件上传,我们还可以通过文件分片的方式来实现。具体做法是将文件分成多个小块,然后通过HTTP协议逐个小块地上传。在客户端,我们可以使用Web API中的File API对文件进行分割,并使用XMLHttpRequest或Fetch API向服务器发送分块的数据,在服务器端我们可以使用Node.js中的multer中间件来实现文件合并。

代码如下所示:

// client.js
const CHUNK_SIZE = 1024 * 1024; // 1MB
function uploadFile(file) {
 const totalChunks = Math.ceil(file.size / CHUNK_SIZE);
 let currentChunk = 0;
 const sendChunk = (start, end, blob, fileId) => {
  const xhr = new XMLHttpRequest();
  xhr.open('POST', `/upload/${fileId}/${start}/${end}`);
  xhr.onload = () => {
   if (xhr.status === 200) {
    const response = JSON.parse(xhr.responseText);
    if (response.ok) {
     currentChunk++;
     if (currentChunk < totalChunks) {
      const start = currentChunk * CHUNK_SIZE;
      const end = Math.min(start + CHUNK_SIZE, file.size);
      const blob = file.slice(start, end);
      sendChunk(start, end, blob, fileId);
     } else {
      console.log('Upload complete!');
     }
    } else {
     console.error(response.error);
    }
   } else {
    console.error(`Error: ${xhr.status} ${xhr.statusText}`);
   }
  };
  xhr.onerror = () => {
   console.error(`Error uploading chunk: ${start}-${end}`);
  };
  xhr.send(blob);
 };
 const fileId = Date.now();
 const start = 0;
 const end = Math.min(CHUNK_SIZE, file.size);
 const blob = file.slice(start, end);
 sendChunk(start, end, blob, fileId);
}

// server.js
const express = require('express');
const multer = require('multer');
const app = express();
const upload = multer({ dest: '/uploads' });
const CHUNK_SIZE = 1024 * 1024; // 1MB
let files = {};
app.post('/upload/:id/:start/:end', upload.single('file'), (req, res) => {
 const id = req.params;
 const chunk = req.file.buffer;
 const chunkLength = chunk.length;
 const fileName = req.file.originalname;
 
 if (!files[id]) {
  files[id] = {
   name: fileName,
   fileType: req.file.mimetype,
   chunks: {},
  };
 }
 files[id].chunks[start] = chunk;
 if (end - start + 1 === chunkLength) {
  const missingChunks = [];
  for (let i = 0; i < Math.ceil(req.file.size / CHUNK_SIZE); i++) {
   if (!files[id].chunks[i * CHUNK_SIZE]) {
    missingChunks.push(i);
   }
  }
  if (missingChunks.length === 0) {
   const data = [];
   for (let i = 0; i < Math.ceil(req.file.size / CHUNK_SIZE); i++) {
    data.push(files[id].chunks[i * CHUNK_SIZE]);
   }
   const file = Buffer.concat(data);
   delete files[id];
   res.status(200).send({ ok: true });
   fs.writeFile(`/uploads/${id}-${fileName}`, file, (err) => {
    if (err) {
     return console.error(err);
    }
    console.log(`File '${fileName}' uploaded successfully!`);
   });
  } else {
   res.status(200).send( ok: false);
  }
 }
});
app.listen(3000, () => {
 console.log('Server is running on http://localhost:3000');
});

通过以上两种方式,我们可以有效地实现大文件上传,提高上传的速度和可靠性,为文件上传功能的实现提供更好的支持。

  
  

评论区