由于想将服务器的内容备份一下,但是使用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 的成功回调
可以尝试加下好友可以交流