21xrx.com
2024-12-22 21:10:52 Sunday
登录
文章检索 我的文章 写文章
Node.js 实现大文件上传
2023-07-11 20:29:22 深夜i     --     --
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');

});

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

  
  

评论区

{{item['qq_nickname']}}
()
回复
回复