Node.js 操作百度网盘实现文件上传(小文件上传,大文件分片上传)
前提准备:获取百度网盘的授权码
https://pan.baidu.com/union/doc/al0rwqzzl
const fs = require('fs');
const crypto = require('crypto');
const path = require('path');
const FormData = require('form-data');
const axios = require('axios');const access_token = '需要自己创建应用获取授权码'async function readFile(filePath) {return new Promise((resolve, reject) => {fs.readFile(filePath, (err, data) => {if (err) {reject(err);} else {resolve(data);}});});
}function getFileInfo(filePath) {return new Promise((resolve, reject) => {fs.stat(filePath, (err, stats) => {if (err) {reject(err);} else {resolve({size: stats.size,isFile: stats.isFile(),name: path.basename(filePath)});}});});
}function getSlice(filePath, start, end) {return new Promise((resolve, reject) => {const sliceStream = fs.createReadStream(filePath, {start,end});let chunk = '';sliceStream.on('data', (data) => {chunk += data;});sliceStream.on('end', () => {resolve(chunk);});sliceStream.on('error', (err) => {reject(err);});});
}async function getFileMd5List(filePath) {try {const fileInfo = await getFileInfo(filePath);const sliceSize = 4 * 1024 * 1024; // 4MBif (fileInfo.size <= sliceSize) {// 如果文件小于等于4MB,直接计算整个文件的MD5并返回const fileData = await readFile(filePath);const fileMd5 = crypto.createHash('md5').update(fileData).digest('hex');return JSON.stringify([fileMd5]);} else {// 如果文件大于4MB,分片计算MD5const sectionCount = Math.ceil(fileInfo.size / sliceSize);const md5List = [];for (let i = 0; i < sectionCount; i++) {const sliceData = await getSlice(filePath, i * sliceSize, (i + 1) * sliceSize - 1);const sliceMd5 = crypto.createHash('md5').update(sliceData).digest('hex');md5List.push(sliceMd5);}return JSON.stringify(md5List);}} catch (error) {console.error('Error in getFileMd5List:', error);return null;}
}async function uploadFileToBaidu(filePath, accessToken) {try {const fileInfo = await getFileInfo(filePath);let md5List = await getFileMd5List(filePath);console.log(md5List)const precreateRes = await axios.post('https://pan.baidu.com/rest/2.0/xpan/file', {path: '/apps/后台管理系统/' + fileInfo.name,size: fileInfo.size,isdir: fileInfo.isFile ? 0 : 1,rtype: 1,autoinit: 1,block_list: md5List}, {params: {method: 'precreate',access_token: accessToken},headers: {'Content-Type': 'application/x-www-form-urlencoded'}});const uploadId = precreateRes.data.uploadid;if (fileInfo.isFile && fileInfo.size <= 4 * 1024 * 1024) {// 小文件上传const form = new FormData();form.append('file', fs.createReadStream(filePath));const uploadSingle = await axios.post('https://d.pcs.baidu.com/rest/2.0/pcs/file', form, {params: {method: 'upload',access_token: accessToken,path: '/apps/后台管理系统/' + fileInfo.name,ondup: 'overwrite'},headers: {...form.getHeaders(),}});return;} else {const sliceSize = 4 * 1024 * 1024;const sectionCount = Math.ceil(fileInfo.size / sliceSize);md5List = []for (let i = 0; i < sectionCount; i++) {const form = new FormData();form.append('file', await getSlice(filePath, i * sliceSize, (i + 1) * sliceSize - 1));if (i === sectionCount - 1) {form.append('file', await getSlice(filePath, i * sliceSize, fileInfo.size - 1));}const sectionItem = await axios.put('https://d.pcs.baidu.com/rest/2.0/pcs/superfile2', form, {params: {method: 'upload',access_token: accessToken,type: 'tmpfile',path: '/apps/后台管理系统/' + fileInfo.name,uploadid: uploadId,partseq: i},headers: {...form.getHeaders(),}});md5List.push(sectionItem.data.md5)}console.log(md5List)const uploadArray = await axios.post('https://pan.baidu.com/rest/2.0/xpan/file', {path: '/apps/后台管理系统/' + fileInfo.name,size: fileInfo.size,isdir: fileInfo.isFile ? 0 : 1,uploadid: uploadId,block_list: JSON.stringify(md5List)}, {params: {method: 'create',access_token: accessToken},headers: {'User-Agent': 'pan.baidu.com','Content-Type': 'application/x-www-form-urlencoded',},});console.log(uploadArray.data)}} catch (e) {console.log('error', e)}
}// uploadFileToBaidu(path.resolve(__dirname, './WeChatProjects.zip'), access_token)
// uploadFileToBaidu(path.resolve(__dirname, './auth.js'), access_token)