由于想将服务器的内容备份一下,但是使用oos是要钱的(没钱),况且现在百度网盘是有会员的,所以就直接上传到百度网盘作为备份就可以了,使用nodejs实现
发现好多人都在找这个文章,放一个之前的代码链接供参考
luyuan/baiduwangpan: 百度网盘上传 – baiduwangpan – pplokijuhyg个人git站点 (theluyuan.com)
需要先获取token ,这个需要接入百度开放平台,因为不属于这里的讨论范畴,就不着重讨论,只重点讲解上传。
根据官网说明:需要实现预上传、分片上传、创建文件。只有完成这三步,才能将文件上传到网盘。
预上传
只要请求接口就要使用到一个库 axios
先安装axios
npm install axios
接口地址https://pan.baidu.com/rest/2.0/xpan/file?method=precreate
参数需要几
path是上传文件路径 在网盘里面的 需要进行编码 在node里面就是使用encodeURI()函数处理一下
size 这个是文件大小,下面将详细讨论如何在node中获取文件大小。
isdir 是不是文件或目录 0 是文件 1是目录 上传时选择对应的格式
autoinit 固定值1
rtype 重命名逻辑 注意下文档描述
block_list 重点,分片的md5,下面也会讲解如何获取分片
获取文件大小
fs.stat()
这个函数在node中是获取文件信息 文档地址
先在全局设置分片大小 const size = 4 * 1024 * 1024
4mb 这是网盘规定的,而且第一个分片不支持小于4m
获取文件大小就是
return new Promise((res, e) => { fs.stat("./a.exe", {}, (err, stats) => { if (!err) { daxiao = stats.size let fenpian = Math.ceil(stats.size / size) res({ size: daxiao, num: fenpian }) } else { e("文件路径错误或文件不存在") } }) })
我封装了一个promise 然后返回是size就是大小 num就是分片数量
另一个重点是如何获取分片md5。
function getfile({ i, url }) { return new Promise((res) => { let data = ''; let start = i * size let end = (i + 1) * size - 1 const stream = fs.createReadStream(url, { start, end }) stream.on('data', (chunk) => { // console.log(接收到 ${chunk.length} 个字节的数据
); if (!data) { data = chunk } else { data = Buffer.concat([data, chunk]) } }); stream.on('end', () => { res(data) }); }) }
这个函数是获取分片内容,根据上面的函数可以拿到一共有多少分片,然后就可以循环这个函数,传入第几个和文件地址,会返回buffer
burrefr不能进行相加,会转变成字符串,然后大小就不是之前的那样了,必须使用Buffer.concat
。
然后就是获取md5。
这里使用到了crypto
文档地址是 http://nodejs.cn/api/crypto.html#crypto_crypto_createhash_algorithm_options
const crypto = require('crypto'); async function getmd5list(url, info) { promistlist = [] // fs.createReadStream for (let i = 0; i < info.num; i++) { let data = await getfile({ url, i }) const hash = crypto.createHash('md5'); // 创建一个md5加密的hash hash.update(data); // 更新内容 const md5 = hash.digest('hex'); // 返回计算内容 console.log(md5); promistlist.push(md5) } return promistlist }
这就是计算md5的方法。
然后md5的列表就会被拿到了。
现在所有内容都拿完整了,下面就是预上传了。
开始预上传
拼接一下参数进行上传
async function precreate(info, url) { let block_list = await getmd5list(url, info) block_list = JSON.stringify(block_list) let data = { path: encodeURI('/app/服务器备份/baidu.exe'), //这是你的上传地址 需要进行url编码 size: info.size, //上传大小 isdir: 0, // 是不是文件夹 0 文件 1 文件夹 autoinit: 1, block_list, // md5的list 注意按顺序 rtype: 1, } let str = "" // 这个需要拼接成formdata格式的,所以这面就手动拼接了 for (let i in data) { str += ${i}=${data[i]}&
} axios.post('https://pan.baidu.com/rest/2.0/xpan/file?method=precreate&access_token=这是你的token', str).then((res) => { // 如果成功的话就是会返回 uploadid 和 block_list // uplpadid就是下面上传的id block_list就是要上传的分片 upload({ url, info, uploadid: res.data.uploadid, pian: res.data.block_list }) }) }
到此预上传就完成了
下面就是上传分片了。
分片上传
async function upload({ url, info, uploadid, pian }) { for (let i of pian) { let data = await upgetfile({ url, i }) await uploadfile(i,data,uploadid) // 循环上传需要上传的分片 } // https://pan.baidu.com/rest/2.0/xpan/file?method=create // 下面的操作是上传完成合成文件 let data = { path:encodeURI('/app/服务器备份/baidu.exe'), // 与上面那个一定要相同 而且还是需要url编码 size: daxiao, // 这个就是上传的大小,而且是总大小,不是分片的大小 isdir:0, rtype: 1, uploadid, // 这个就是上面返回的uploadid block_list:JSON.stringify(promistlist) // 这个就是上传的MD5列表,还是全部的 } let s = '' // 需要formdata格式 for(let i in data){ s += i + "=" + data[i] + '&' } console.log(s) axios.post('https://pan.baidu.com/rest/2.0/xpan/file?method=create',s,{ params:{ access_token: '你的token', } }).then((res)=>{ console.log(res) }) }
上面的uploadfile 是上传 下面的是拼接文件 不进行拼接文件是不能完成上传的。
function uploadfile(i,data,uploadid){ return new Promise((r)=>{ console.log(data.length) let urls = '/rest/2.0/pcs/superfile2?' // 请求地址 let params = { access_token: 'token', // 你的token method: 'upload', // 默认 type: "tmpfile", // 默认 path: encodeURI('/app/服务器备份/baidu.exe'), //上传地址 与之前的相同 uploadid, //还是返回的uploadid partseq: i // 这个是分片的编号 } for (let j in params) { urls += j + '=' + params[j] + '&' } // 下面由于我用axios上传失败了 改用httpsrequest const options = { hostname: 'd.pcs.baidu.com', port: 443, path: urls, method: 'PUT', // 这面官网说是post 但是我试了下必须put 不然一直错 headers: { 'content-type':'multipart/form-data', //这是格式 'Content-Length': data.length // 这是主体长度 } }; const req = https.request(options, (res) => { console.log('状态码:', res.statusCode); console.log('请求头:', res.headers); res.on('data', (d) => { process.stdout.write(d); }); r(1) }); req.on('error', (e) => { console.error(e); }); req.write(data); // 写入数据 req.end(); //写入完成一定要执行end }) }
到现在为止就是上传成功了。
其实挺简单的,但是网上很少有nodejs的资料,如果你看到感觉还是有问题可以留言我继续完善,或者是加我qq
36行的r(1) 是啥?
promise 的成功回调
可以尝试加下好友可以交流