下面是“基于Node.js的大文件分片上传示例”的完整攻略及两条示例说明。
简介
当上传大文件时,可能会遇到一些问题,例如网络不稳定、上传时间长等。大量数据上传时,还需要使用分片上传技术,避免将整个文件发送到服务器。在这里,我们将介绍如何使用Node.js实现大文件分片上传。
实现步骤
安装依赖
首先,我们需要先安装依赖包,这里我们使用multiparty
和formidable
来处理上传的表单。
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技术站