基于Node.js的大文件分片上传示例

yizhihongxing

下面是“基于Node.js的大文件分片上传示例”的完整攻略及两条示例说明。

简介

当上传大文件时,可能会遇到一些问题,例如网络不稳定、上传时间长等。大量数据上传时,还需要使用分片上传技术,避免将整个文件发送到服务器。在这里,我们将介绍如何使用Node.js实现大文件分片上传。

实现步骤

安装依赖

首先,我们需要先安装依赖包,这里我们使用multipartyformidable来处理上传的表单。

npm install multiparty formidable --save

实现服务端代码

我们可以通过以下代码来实现服务端的功能:

const http = require('http');
const url = require('url');
const fs = require('fs');
const { parse } = require('querystring');
const formidable = require('formidable');

http.createServer((req, res) => {
  const { pathname } = url.parse(req.url);

  if (pathname === '/') {
    if (req.method === 'GET') { // 获取页面
      fs.readFile('./index.html', (err, data) => {
        res.end(data);
      });
    } else if (req.method === 'POST') { // 上传文件
      const form = new formidable.IncomingForm();
      form.parse(req, (err, fields, files) => {
        if (err) throw err;
        console.log(fields); // 输出表单中传递的其他参数
        console.log(files); // 文件的相关信息,包括路径、大小等
        res.end('upload success!');
      });
    }
  } else { // 处理其他请求
    res.statusCode = 404;
    res.end('not found');
  }
}).listen(3000, () => {
  console.log('server is running at http://localhost:3000');
});

该服务端代码实现了获取首页和上传文件的功能,我们使用formidable中的IncomingForm实例来处理上传的表单数据,具体的文件上传过程将在下面的示例中介绍。

实现客户端代码

在客户端页面中,需要使用JavaScript来实现文件上传功能:

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <title>大文件分片上传示例</title>
</head>

<body>
  <h1>大文件分片上传示例</h1>
  <form id="uploadForm" method="post" enctype="multipart/form-data">
    <input type="text" name="name" placeholder="请输入名字"><br>
    <input type="file" name="file"><br>
    <button type="submit">上传</button>
  </form>
  <script>
    const form = document.getElementById('uploadForm');
    form.addEventListener('submit', (event) => {
      event.preventDefault();

      // 获取需要上传的文件
      const file = form.elements['file'].files[0];
      console.log(file);

      // 分片上传代码在这里实现
    });
  </script>
</body>

</html>

在这里,我们使用了HTML5新增的FormData API来实现文件上传,上传的文件会包装在FormData对象中,然后通过ajax提交到服务器。

示例

示例一:上传文件到七牛云

在这个示例中,我们将使用qiniu库,将文件上传到七牛云存储中。

const qiniu = require('qiniu');
const path = require('path');
const { accessKey, secretKey, bucket, domain } = require('./config');

const mac = new qiniu.auth.digest.Mac(accessKey, secretKey);
const options = {
  scope: bucket,
};
const putPolicy = new qiniu.rs.PutPolicy(options);
const uploadToken = putPolicy.uploadToken(mac);
const config = new qiniu.conf.Config();
const formUploader = new qiniu.form_up.FormUploader(config);

function uploadChunk(fileKey, localFile, uploadId, chunkSize, chunkNum, offset, progressCallback) {
  return new Promise((resolve, reject) => {
    const putExtra = new qiniu.form_up.PutExtra();
    putExtra.progressCallback = progressCallback;

    formUploader.putFile(uploadToken, fileKey, localFile, putExtra, (err, _, info) => {
      if (err) {
        reject(err);
        return;
      }

      if (info.statusCode === 200) {
        console.log(`Part ${chunkNum} uploaded successfully.`);
        const uploadedChunkInfo = { 
          etag: info.etag,
          partNumber: chunkNum,
        };
        resolve(uploadedChunkInfo);
      } else {
        reject(info);
      }
    });
  });
}

function uploadFile(localFile, fileKey) {
  return new Promise((resolve, reject) => {
    qiniu.rpc.uploadId(mac, bucket, fileKey).then(uploadId => {
      const filesize = getFilesizeSync(localFile);
      const chunkSize = filesize < chunkSizeLimit ? filesize : chunkSizeLimit;
      const chunkNum = Math.ceil(filesize / chunkSize);

      // 分片上传代码
      const promises = Array(chunkNum).fill().map((_, index) => {
        const start = index * chunkSize;
        const end = (index + 1) * chunkSize - 1 > filesize ? filesize : (index + 1) * chunkSize;
        const offset = start;

        const chunkFile = path.resolve(chunkDir, `${fileKey}.${uploadId}.${index}`);
        return uploadChunk(fileKey, chunkFile, uploadId, chunkSize, index + 1, offset, (percent) => {
          console.log(percent.toFixed(2) + '%');
        });
      });

      Promise.all(promises).then(partsInfo => {
        const parts = partsInfo.map(part => {
          return {
            partNumber: part.partNumber,
            etag: part.etag,
          };
        });

        const sortedParts = parts.sort((a, b) => a.partNumber - b.partNumber);

        // 完成上传
        qiniu.rpc.completeMultipartUpload(mac, bucket, fileKey, uploadId, sortedParts).then(result => {
          console.log(result);
          resolve(result);
        }).catch(err => {
          reject(err);
        });
      }).catch(err => {
        reject(err);
      });
    }).catch(err => {
      reject(err);
    });
  });
}

const localFile = '/path/to/local/file';
const fileKey = 'test.jpg';
uploadFile(localFile, fileKey)
.then(res => {
  console.log(res);
})
.catch(err => {
  console.error(err);
});

示例二:上传文件到阿里OSS

在这个示例中,我们将使用ali-oss库,将文件上传到阿里云OSS中。

const path = require('path');
const OSS = require('ali-oss');
const { accessKeyId, accessKeySecret, bucket } = require('./config');

const client = new OSS({
  region: 'oss-cn-beijing',
  accessKeyId,
  accessKeySecret,
  bucket,
});

function uploadChunk(fileKey, localfile, partNum, uploadId, fromByte, toByte, progressCallback) {
  return new Promise((resolve, reject) => {
    const options = {
      partNumber: partNum,
      uploadId,
      fromByte,
      toByte,
      progress: progressCallback,
    };
    client.uploadPart(fileKey, localfile, options).then(result => {
      const part = {
        PartNumber: options.partNumber,
        ETag: result.res.headers.etag,
      };
      resolve(part);
    }).catch(err => {
      reject(err);
    });
  });
}

function uploadFile(localFile, fileKey, fileSize) {
  return new Promise((resolve, reject) => {
    client.initMultipartUpload(fileKey, { timeout: 3600000 }).then(result => {
      const uploadId = result.uploadId;
      const chunkSize = 1024 * 1024;
      const chunkCount = Math.ceil(fileSize / chunkSize);
      const partList = Array.apply(null, {length: chunkCount}).map((_, index) => {
        const haveRead = (index * chunkSize);
        const toByte = Math.min((index + 1) * chunkSize, fileSize) - 1;
        const options = {
          partNumber: index + 1,
          fromByte: haveRead,
          toByte,
        };
        return options;
      });

      // 上传分片
      const promises = partList.map((part, index) => {
        const start = index * chunkSize;
        const end = (index + 1) * chunkSize - 1 >= fileSize ? fileSize : (index + 1) * chunkSize;
        const offset = start;
        const chunkFile = path.resolve(chunkDir, `${fileKey}.${uploadId}.${index}`);
        return uploadChunk(fileKey, chunkFile, part.partNumber, uploadId, start, end, percent => {
          console.log(percent);
        });
      });

      Promise.all(promises).then(parts => {
        console.log(parts);
        client.completeMultipartUpload(fileKey, uploadId, parts).then(result => {
          console.log(result);
        }).catch(err => {
          reject(err);
        });
      }).catch(err => {
        console.error(err);
      });
    }).catch(err => {
      reject(err);
    });
  });
}

const localFile = '/path/to/local/file';
const fileKey = 'test.jpg';
const fileSize = getFilesizeSync(localFile);
uploadFile(localFile, fileKey, fileSize).then(result => {
  console.log(result);
}).catch(err => {
  console.error(err);
});

以上便是“基于Node.js的大文件分片上传示例”的完整攻略,希望对您有所帮助。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:基于Node.js的大文件分片上传示例 - Python技术站

(0)
上一篇 2023年6月8日
下一篇 2023年6月8日

相关文章

  • 详解Node.js access_token的获取、存储及更新

    详解Node.js access_token的获取、存储及更新 在开发微信公众号等基于微信平台的应用时,我们通常需要使用access_token进行接口调用。本文将详细介绍Node.js获取、存储和更新access_token的完整攻略。 获取access_token 获取access_token需要向微信服务器发送GET请求,具体的接口地址为: https…

    node js 2023年6月8日
    00
  • 服务端nodejs抓取jsonp接口数据实现示例

    下面就是关于服务端 Node.js 抓取 JSONP 接口数据实现示例的攻略。 首先,需要明确一点:JSONP 跨域请求是基于 JavaScript 的,它通过动态创建 <script> 标签实现。而 Node.js 是以服务器形式对外提供服务的,使用 JavaScript 编写,所以本身 Node.js 对于 JSONP 请求并不支持。 但是我…

    node js 2023年6月8日
    00
  • 详解如何使用koa实现socket.io官网的例子

    下面是详解如何使用koa实现socket.io官网的例子的完整攻略。 准备工作 首先,安装koa和socket.io的模块包。使用命令行进入到你要进行开发的项目文件夹中,然后执行以下命令: npm install –save koa socket.io 简单例子 以一个简单的例子来展示如何使用koa和socket.io的组合实现官网的例子。 首先,在应用程…

    node js 2023年6月8日
    00
  • 深入理解Node module模块

    深入理解Node module模块 在 Node.js 中, module 模块是一个核心概念。为了更好的理解和使用 Node.js,我们有必要深入了解 Node module 模块。 什么是 module 模块? module 模块是 Node.js 中一个核心概念,用于封装和组织代码。在 Node.js 中,几乎任何的 JavaScript 文件都可以被…

    node js 2023年6月8日
    00
  • javascript循环链表之约瑟夫环的实现方法

    当我们在处理需要循环的数据时,循环链表是一种非常常见的数据结构。而约瑟夫环是一个经典的可用于解决Josephus问题的算法,即在一个有限的环中每隔k个(k > 1)数杀掉一个人,直到剩下最后一个人。在 JavaScript 中,我们可以用循环链表来实现该算法。 首先,我们需要定义一个循环链表数据结构 循环链表由链表头和尾组成,头尾相接即为循环链表。我们…

    node js 2023年6月8日
    00
  • VsCode与Node.js知识点详解

    VsCode与Node.js知识点详解 什么是VsCode VsCode是一款由微软开发的跨平台轻量级代码编辑工具,支持多种编程语言,如JavaScript、TypeScript、C#等等。VsCode 的主要特点包括快速响应、智能提示、代码片段、语法高亮、扩展性强等,凭借其丰富的插件库和良好的用户体验,已经成为许多开发者的首选编辑器。 什么是Node.js…

    node js 2023年6月7日
    00
  • 使用node.js 制作网站前台后台

    使用Node.js制作网站前台后台是非常流行的Web开发技术,它可以帮助我们简化网站开发过程,提高开发效率和用户体验。下面是具体步骤: 确定网站开发需求与预期 在开始开发Node.js的网站前台后台之前,需要认真考虑网站的开发需求和预期。确定这些需求和预期可以帮助我们更好的规划开发流程,从而避免在后期开发过程中浪费时间和精力。 确定后端技术框架 如果要使用N…

    node js 2023年6月8日
    00
  • node.js 全局变量的具体使用

    当我们编写Node.js代码时,我们经常需要在多个模块之间共享数据或者函数,这时候就需要用到Node.js的全局变量。 Node.js中的全局变量包括:__dirname、__filename、exports、module、process等。 下面将详细讲解全局变量的具体使用: 1. __dirname和__filename变量 __dirname和__fi…

    node js 2023年6月8日
    00
合作推广
合作推广
分享本页
返回顶部