更新时间:2025-04-01 GMT+08:00
多段相关接口说明(Node.js SDK)
对于较大文件上传,可以切分成段上传。用户可以在如下的应用场景内(但不仅限于此),使用分段上传的模式:
- 上传超过100MB大小的文件。
- 网络条件较差,和OBS服务端之间的链接经常断开。
- 上传前无法确定将要上传文件的大小。
分段上传分为如下3个步骤:
分段上传的主要目的是解决大文件上传或网络条件较差的情况。下面的代码示例展示了如何使用分段上传并发上传大文件:
// 引入obs库
// 使用npm安装
const ObsClient = require('esdk-obs-nodejs');
// 使用源码安装
// var ObsClient = require('./lib/obs');
const fs = require('fs');
// 创建ObsClient实例
var obsClient = new ObsClient({
// 推荐通过环境变量获取AKSK,这里也可以使用其他外部引入方式传入,如果使用硬编码可能会存在泄露风险
// 您可以登录访问管理控制台获取访问密钥AK/SK,获取方式请参见https://support.huaweicloud.com/intl/zh-cn/usermanual-ca/ca_01_0003.html
access_key_id: process.env.ACCESS_KEY_ID,
secret_access_key: process.env.SECRET_ACCESS_KEY,
// 【可选】如果使用临时AK/SK和SecurityToken访问OBS,同样建议您尽量避免使用硬编码,以降低信息泄露风险。您可以通过环境变量获取访问密钥AK/SK,也可以使用其他外部引入方式传入
// security_token: process.env.SECURITY_TOKEN,
// endpoint填写Bucket对应的Endpoint, 这里以中国-香港为例,其他地区请按实际情况填写
server: "https://obs.ap-southeast-1.myhuaweicloud.com"
});
// 指定存储桶名称
const Bucket = 'bucketname'
// 指定对象名,此处以 example/objectname 为例
const Key = 'objectname'
// 指定分段大小
const DEFAULT_PART_SIZE = 9 * 1024 * 1024;
// 指定并发数为20
const CONCURRENCY = 20
// 准备分段上传的参数
const preparePartParams = (Bucket, Key, UploadId) => (sampleFile, partSize = DEFAULT_PART_SIZE) => {
try {
const fileSize = fs.lstatSync(sampleFile).size;
const partCount = fileSize % partSize === 0 ? Math.floor(fileSize / partSize) : Math.floor(fileSize / partSize) + 1;
const uploadPartParams = [];
// 指定并发上传
for (let i = 0; i < partCount; i++) {
// 分段在文件中的起始位置
let Offset = i * partSize;
// 分段大小
let currPartSize = (i + 1 === partCount) ? fileSize - Offset : partSize;
// 分段号
let PartNumber = i + 1;
uploadPartParams.push({
Bucket,
Key,
PartNumber,
UploadId,
Offset,
SourceFile: sampleFile,
PartSize: currPartSize,
});
};
return ({ uploadPartParams, fileSize });
} catch (error) {
console.log(error)
};
};
/**
* uploadSuccessSize:已经上传成功的大小
* uploadSuccessCount:已经上传成功的段数量
* concurrency:当前并发数
*/
let uploadSuccessSize = 0;
let uploadSuccessCount = 0;
let concurrency = 0
const parts = [];
// 上传段
const uploadPart = (uploadPartParam, otherUploadPartInfo) => {
const partCount = otherUploadPartInfo.partCount;
const fileSize = otherUploadPartInfo.fileSize;
concurrency++;
return obsClient
.uploadPart(uploadPartParam)
.then(result => {
const { PartNumber, PartSize } = uploadPartParam;
if (result.CommonMsg.Status < 300) {
uploadSuccessCount++;
uploadSuccessSize += PartSize;
// 打印上传进度
console.log(`the current concurrent count is ${concurrency} | uploaded segment: ${uploadSuccessCount}/${partCount}. the progress is ${((uploadSuccessSize / fileSize) * 100).toFixed(2)}% | the partNumber ${PartNumber} upload succeeded.`);
parts.push({ PartNumber, ETag: result.InterfaceResult.ETag });
} else {
console.log(result.CommonMsg.Code, parts);
};
concurrency--;
}).catch(function (err) {
console.log(err);
throw err;
})
};
// 分段上传
const uploadFile = (sourceFile) => {
// 初始化分段任务
obsClient.initiateMultipartUpload({
Bucket,
Key
}).then(res => {
const Status = res.CommonMsg.Status;
const UploadId = res.InterfaceResult.UploadId;
if (typeof Status === 'number' && Status > 300) {
console.log(`initiateMultipartUpload failed! Status:${Status}`);
return;
};
const partParams = preparePartParams(Bucket, Key, UploadId)(sourceFile);
const uploadPartParams = partParams.uploadPartParams;
const fileSize = partParams.fileSize;
const partCount = uploadPartParams.length;
const otherUploadPartInfo = { fileSize, partCount };
// 调用并行上传函数
parallelFunc(uploadPartParams, (param) => uploadPart(param, otherUploadPartInfo), CONCURRENCY)
.then(() => {
// 合并段
obsClient.completeMultipartUpload({
Bucket,
Key,
UploadId,
Parts: parts.sort((a, b) => a.PartNumber - b.PartNumber)
}, (err, result) => {
if (err) {
console.log('Error-->' + err);
} else {
console.log('Status-->' + result.CommonMsg.Status);
};
});
});
}).catch(function (err) {
console.log(err)
});
};
/**
* 实现函数并行执行
* @param {Array} params 回调函数的参数数组
* @param {Promise} promiseFn 回调函数
* @param {number} limit 并行数
*/
const parallelFunc = (params, promiseFn, limit) => {
return new Promise((resolve) => {
let concurrency = 0;
let finished = 0;
const count = params.length;
const run = (param) => {
concurrency++;
promiseFn(param)
.then(() => {
concurrency--;
drainQueue();
finished++;
if (finished === count) {
resolve();
};
});
};
const drainQueue = () => {
while (params.length > 0 && concurrency < limit) {
var param = params.shift();
run(param);
};
};
drainQueue();
});
};
uploadFile('localfile');
大文件分段上传时,使用Offset参数和PartSize参数配合指定每段数据在文件中的起始结束位置。
并发数过大,可能会因为网络不稳定等原因,产生Timeout错误,需要限制并发数。
父主题: 多段相关接口(Node.js SDK)