文章目录
- 前言
- 一、创建百度网盘账号
- 二、代码实现
- 1. 常量类
- 2. 工具类
- 3. 授权码模式授权
- 4. 文件分片上传(可获取进度)--方法一
- 5. 文件下载(可获取进度)--方法一
- 6. 获取文件列表
- 7. 文件分片上传(不可获取进度)--方法二
- 7. 文件下载(不可获取进度)--方法二
前言
百度网盘官方文档中的介绍的很清楚明了,在此不做赘述,直接上代码!!!
提示:以下是案例是基于企业认证的账号
一、创建百度网盘账号
百度网盘官方文档
- 进行百度帐号的实名认证;
- 认证成功后,创建应用;
在【控制台】中点击「+创建应用」创建一个应用
- 完成应用创建后,你的应用可以获取到关键凭证:Appid、Appkey、Secretkeyk和Signkey。
- 设置授权的回调地址,如果不设置回调地址那么使用默认的回调地址oob;
【应用详情】页,右上角【安全设置】,设置授权回调地址
- 授权回调地址支持配置多个,多个地址之间,通过英文逗号进行分隔。
- 授权回调地址修改后在 1 小时后生效。
- 授权回调地址中如果包含特殊符号,请求接口时,回调地址需要进行url编码。
二、代码实现
1. 常量类
package boot.test.constant;
/**
* 百度网盘常量信息
*/
public interface BaiduNetDiskConstant {
/**
* 百度网盘 秘钥字段
*/
String APPKEY = "AppKey";
String SECRETKEY = "SecretKey";
String ACCESSTOKEN = "AccessToken";
/**
* 文件上传到百度网盘的路径地址
* - 每个第三方应用在网盘只能拥有一个文件夹用于存储上传文件,该文件夹必须位于/apps目录下,apps下的文件夹名称为申请接入时填写的申请接入的产品名称。
* - 如申请接入的产品名称为云存储,那么该文件夹为/apps/云存储,用户看到的文件夹为/我的应用数据/云存储。
*/
String BAIDUNETDISK_APP_PATH = "/apps/XXX/";
/**
* 分片上传 单片大小 单位:MB
* - 上传的文件大于4MB,需要将上传的文件按照4MB大小在本地切分成分片,不足4MB的分片自动成为最后一个分片
*/
Integer BAIDUNETDISK_SLICE_SIZE = 4;
/**
* 百度网盘 回调地址 URL
*/
String BAIDUNETDISK_CALLBACK_URL = "https://internal.beausoft.cn";
/**
* 百度网盘用户授权码的 URL
*/
String BAIDUNETDISK_ACCREDITCODE_URL = "https://openapi.baidu.com/oauth/2.0/authorize?";
/**
* 1. 获取 Access Token的 URL
* 2. 刷新Access Token的 URL
*/
String BAIDUNETDISK_ACCESSTOKEN_URL = "https://openapi.baidu.com/oauth/2.0/token?";
/**
* 百度网盘用户信息 URL
*/
String BAIDUNETDISK_USERINFO_URL = "https://pan.baidu.com/rest/2.0/xpan/nas?";
/**
* 百度网盘容量信息 URL
*/
String BAIDUNETDISK_QUOTA_URL = "https://pan.baidu.com/api/quota?";
/**
* 1. 百度网盘 文件 列表 URL
* 2. 百度网盘 文档 列表 URL
*/
String BAIDUNETDISK_FILE_LIST_URL = "https://pan.baidu.com/rest/2.0/xpan/file?";
/**
* 分片上传 - 预上传 URL
*/
String BAIDUNETDISK_PREUPLOAD_URL = "https://pan.baidu.com/rest/2.0/xpan/file?";
/**
* 分片上传 - 分片上传 URL
*/
String BAIDUNETDISK_FILE_UPLOAD_URL = "https://d.pcs.baidu.com/rest/2.0/pcs/superfile2?";
/**
* 分片上传 - 合并文件 URL
*/
String BAIDUNETDISK_MERGE_FILE_URL = "https://pan.baidu.com/rest/2.0/xpan/file?";
/**
* 单步上传 URL
*/
String BAIDUNETDISK_SINGLE_UPLOAD_URL = "https://d.pcs.baidu.com/rest/2.0/pcs/file?";
/**
* 百度网盘 查询文件信息 URL
*/
String BAIDUNETDISK_MULTIMEDIA_URL = "http://pan.baidu.com/rest/2.0/xpan/multimedia?";
/**
* 百度网盘 创建文件夹 URL
*/
String BAIDUNETDISK_CREATE_FOLDER_URL = "https://pan.baidu.com/rest/2.0/xpan/file?";
}
2. 工具类
package boot.test.utils;
import java.net.URLEncoder;
public class BaiduNetDiskUtil {
/**
* 对中文字符进行URL编码
*
* @param encodeStr
* @return
*/
public static String urlEncoder(String encodeStr) {
try {
String encodedString = URLEncoder.encode(encodeStr, "UTF-8");
return encodedString;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 字节转GB
*
* @param len
* @return
*/
public static String byteConvertToGib(Long len) {
if (len == null || len <= 0) {
return "0GB";
}
try {
double val = len.doubleValue() / (1024 * 1024 * 1024);
return val < 0.1 ? "0.1GB" : String.format("%.1f", val) + "GB";
} catch (Exception e) {
e.printStackTrace();
return "0GB";
}
}
}
MD5工具类
package boot.test.utils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.codec.digest.DigestUtils;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
@Slf4j
public class BigFileMD5Util {
private static MessageDigest MD5 = null;
static {
try {
MD5 = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException ne) {
log.error("init md5 utils failed", ne);
}
}
public static String getMD5(InputStream inputStream) throws IOException {
byte[] buffer = new byte[8192]; // each read bytes to buff
int length;
while ((length = inputStream.read(buffer)) != -1) {
MD5.update(buffer, 0, length);
}
return new String(Hex.encodeHex(MD5.digest()));
}
/**
* 对一个文件获取md5值
*
* @return md5串
*/
public static String getMD5(File file) throws IOException {
try (FileInputStream fileInputStream = new FileInputStream(file)) {
return getMD5(fileInputStream);
} catch (IOException e) {
throw e;
}
}
/**
* 求一个字符串的md5值
*
* @param target 字符串
* @return md5 value
*/
public static String getStringMD5(String target) {
return DigestUtils.md5Hex(target);
}
}
package boot.test.baidu;
import java.io.*;
public class RandomAccessFileInputStream extends InputStream {
private final RandomAccessFile raf;
private final long offset;
private final int size;
private volatile int pos;
public RandomAccessFileInputStream(RandomAccessFile raf, long offset, int size) {
this.raf = raf;
this.offset = offset;
this.size = size;
}
@Override
public int read() throws IOException {
synchronized (raf) {
raf.seek(offset + pos);
pos += 1;
return raf.read();
}
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
synchronized (raf) {
raf.seek(offset + pos);
if (offset + pos >= offset + size) {
return -1;
} else if (offset + pos + len - off >= offset + size) {
byte[] buff = new byte[(int) ((offset + size) - (offset + pos))];
int r = raf.read(buff, 0, buff.length);
System.arraycopy(buff, 0, b, off, buff.length);
pos += r;
return r;
} else {
int r = raf.read(b, off, len);
pos += r;
return r;
}
}
}
}
3. 授权码模式授权
package boot.test.baidu;
import boot.test.constant.BaiduNetDiskConstant;
import boot.test.dto.BaiduNetDiskAccessToken;
import boot.test.utils.BaiduNetDiskUtil;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
/**
* 百度网盘 授权
* - 授权码模式:授权码模式适用于有 Server 端的应用。
*/
@Slf4j
@Component
public class BaiduNetDiskAccredit {
/**
* 获取百度用户授权码的url
*
* @param appKey
* @return
*/
public static String userAccreditCodeUrl(String appKey) {
String url = BaiduNetDiskConstant.BAIDUNETDISK_ACCREDITCODE_URL +
"&response_type=code" + // 固定值,值必须为code。表示为授权码模式。
"&client_id=" + appKey +// 应用的AppKey
"&redirect_uri=" + BaiduNetDiskUtil.urlEncoder(BaiduNetDiskConstant.BAIDUNETDISK_CALLBACK_URL) + // 应用的授权回调地址
"&scope=basic,netdisk";// 固定值,值必须为basic,netdisk。
// "&device_id=APPID"; // 应用的AppID, device_id为硬件应用下的必选参数
// "&qrcode=1" //让用户通过扫二维码的方式登录百度账号时,可传递 “qrcode=1”
log.info("百度用户授权码 url:{} " + url);
return url;
}
/**
* 根据用户授权码 Code 换取 Access Token 凭证
* - Access Token 有效期30天,过期后支持刷新
*
* @param code 用户授权后拿到的code, 获取到的授权码 code 有效期 10 分钟,且仅一次有效。
* @param appKey 应用的AppKey。
* @param secretKey 应用的SecretKey
* @return
*/
public static BaiduNetDiskAccessToken getAccessToken(String code, String appKey, String secretKey) {
BufferedReader in = null;
BaiduNetDiskAccessToken accessTokenDto = null;
try {
URL url = new URL(BaiduNetDiskConstant.BAIDUNETDISK_ACCESSTOKEN_URL +
"grant_type=authorization_code" + // 固定值,值必须为authorization_code。
"&code=" + code + // 获取用户授权后拿到的code。注意code 作为换取Access Token的票据,每次用户授权带上的 code 将不一样,code 只能使用一次,10分钟未被使用自动过期。
"&client_id=" + appKey + //应用的AppKey。
"&client_secret=" + secretKey + // 应用的SecretKey。
"&redirect_uri=" + URLEncoder.encode(BaiduNetDiskConstant.BAIDUNETDISK_CALLBACK_URL, "UTF-8") //授权回调地址。注意必须与获取授权code传递的保持一致。
);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
log.info("获取Access Token响应结果:{} " + response.toString());
accessTokenDto = JSON.parseObject(response.toString(), BaiduNetDiskAccessToken.class);
return accessTokenDto;
} catch (Exception e) {
e.printStackTrace();
System.out.println("获取百度 Access Token 凭证失败");
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e2) {
e2.printStackTrace();
}
}
}
return null;
}
/**
* 刷新Access Token
* - 授权码模式下,刷新后Access Token 的有效期仍为 30 天
* - 刷新请求,如果API返回失败,旧的refresh_token会失效,此时需要重新发起授权请求,获取新的 Access Token、refresh_token,而不是使用旧的 refresh_token 循环再发起刷新请求。
*
* @param refreshToken refresh_token 只支持使用一次,refresh_token 使用后失效,下次刷新 Access Token 时需要使用上一次刷新请求响应中的 refresh_token
* @param appKey 应用的AppKey。
* @param secretKey 应用的SecretKey
* @return
*/
public BaiduNetDiskAccessToken refreshAccessToken(String refreshToken, String appKey, String secretKey) {
BufferedReader in = null;
BaiduNetDiskAccessToken accessTokenDto = null;
try {
URL url = new URL(BaiduNetDiskConstant.BAIDUNETDISK_ACCESSTOKEN_URL +
"grant_type=refresh_token" + //固定值,值必须为 refresh_token。
"&refresh_token=" + refreshToken +//固定值,值必须为换取 Access Token时候返回的 refresh_token 值。
"&client_id=" + appKey + //应用的AppKey
"&client_secret=" + secretKey //应用的SecretKey
);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
log.info("刷新Access Token响应结果:{} " + response.toString());
accessTokenDto = JSON.parseObject(response.toString(), BaiduNetDiskAccessToken.class);
return accessTokenDto;
} catch (Exception e) {
e.printStackTrace();
System.out.println("刷新百度 Access Token 凭证失败");
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e2) {
e2.printStackTrace();
}
}
}
return null;
}
}
package boot.test.dto;
import lombok.Data;
/**
* 百度网盘请求响应
*/
@Data
public class BaiduRespondBase {
/**
* 错误信息
*/
private String errmsg;
/**
* 错误码
*/
private Integer errno;
/**
* 发起请求的请求 Id
*/
private String requestId;
}
package boot.test.dto;
import lombok.Data;
/**
* 百度网盘 Access Token
*/
@Data
public class BaiduNetDiskAccessToken {
/**
* 获取到的Access Token,Access Token是调用网盘开放API访问用户授权资源的凭证。
* Access Token 有效期30天
*/
private String accessToken;
/**
* Access Token的有效期,单位为秒。
*/
private Integer expiresIn;
/**
* 用于刷新 Access Token, 有效期为10年。
*/
private String refreshToken;
/**
* Access Token 最终的访问权限,即用户的实际授权列表。
*/
private String scope;
}
测试
public static void main(String[] args) {
String AppKey = BaiduNetDiskConstant.APPKEY;
String SecretKey = BaiduNetDiskConstant.SECRETKEY;
String accessToken = BaiduNetDiskConstant.ACCESSTOKEN;
/**
* 获取用户授权码URL
*/
String s = BaiduNetDiskAccredit.userAccreditCodeUrl(AppKey);
System.out.println(s);
/**
* 获取 accessToken
* "9683f31834108e1bcd1821ef7d4c30a5" 👆👆👆👆👆👆👆👆👆来源于用户授权码的 code 👆👆👆👆👆👆👆👆👆👆👆👆
*/
BaiduNetDiskAccessToken accessToken1 = BaiduNetDiskAccredit.getAccessToken("9683f31834108e1bcd1821ef7d4c30a5", AppKey, SecretKey);
System.out.println(accessToken1.getAccessToken());
}
4. 文件分片上传(可获取进度)–方法一
package boot.test.baidu;
import boot.test.constant.BaiduNetDiskConstant;
import boot.test.dto.BaiduNetDiskMergeFile;
import boot.test.dto.BaiduNetDiskPrecreate;
import boot.test.utils.BaiduNetDiskUtil;
import boot.test.vo.BaiduUploadFileVo;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.StreamProgress;
import cn.hutool.core.io.resource.Resource;
import cn.hutool.http.Header;
import cn.hutool.http.HttpException;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicLong;
/**
* 百度网盘文件上传类
*/
@Slf4j
public class BaiduNetDiskUpload {
/**
* 分片上传
* -预上传
*
* @param accessToken
* @param baiduUploadFileVo
* @return
*/
public static BaiduNetDiskPrecreate precreate(String accessToken, BaiduUploadFileVo baiduUploadFileVo) {
BaiduNetDiskPrecreate precreateDto = null;
try {
URL url = new URL(BaiduNetDiskConstant.BAIDUNETDISK_PREUPLOAD_URL +
"method=precreate" + // 本接口固定为precreate
"&access_token=" + accessToken //接口鉴权认证参数,标识用户
);
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
httpConn.setRequestMethod("POST");
httpConn.setRequestProperty("User-Agent", "pan.baidu.com");
httpConn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
httpConn.setDoOutput(true);
OutputStreamWriter writer = new OutputStreamWriter(httpConn.getOutputStream());
writer.write("path=" + BaiduNetDiskUtil.urlEncoder(baiduUploadFileVo.getPath()) + //上传后使用的文件绝对路径,需要urlencode
"&size=" + baiduUploadFileVo.getSize() + //文件和目录两种情况:上传文件时,表示文件的大小,单位B;上传目录时,表示目录的大小,目录的话大小默认为0
"&isdir=" + baiduUploadFileVo.getIsdir() + //是否为目录,0 文件,1 目录
"&block_list=[\"" + baiduUploadFileVo.getBlockList() + "\"]" + //文件各分片MD5数组的json串
"&autoinit=1" + //固定值1
"&rtype=3" //文件命名策略。1 表示当path冲突时,进行重命名 2 表示当path冲突且block_list不同时,进行重命名 3 当云端存在同名文件时,对该文件进行覆盖
);
writer.flush();
writer.close();
httpConn.getOutputStream().close();
BufferedReader in = new BufferedReader(new InputStreamReader(httpConn.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
log.info("预上传响应结果:{} " + response.toString());
precreateDto = JSON.parseObject(response.toString(), BaiduNetDiskPrecreate.class);
if (precreateDto == null || precreateDto.getErrno() != 0) {
System.out.println("预上传文件失败");
}
return precreateDto;
} catch (Exception e) {
e.printStackTrace();
System.out.println("预上传文件失败");
}
return null;
}
/**
* 分片上传
* -分片文件
*
* @param accessToken
* @param uploadid 上一个阶段预上传precreate接口下发的uploadid
* @param path 上传后使用的文件绝对路径
* @param file 分片索引
* @return
*/
public static boolean shardUpload(String accessToken, String uploadid, String path, File file) {
try {
RandomAccessFile raf = new RandomAccessFile(file, "r");
long fileSize = file.length();
long splitSize = BaiduNetDiskConstant.BAIDUNETDISK_SLICE_SIZE * 1024 * 1024;//单片文件大小
int count = (int) (fileSize % splitSize != 0 ? fileSize / splitSize + 1 : fileSize / splitSize);
AtomicLong size = new AtomicLong();
for (int i = 0; i < count; i++) {
String url = BaiduNetDiskConstant.BAIDUNETDISK_FILE_UPLOAD_URL +
"method=upload" + //本接口固定为upload
"&access_token=" + accessToken +
"&type=tmpfile" + //固定值 tmpfile
"&path=" + BaiduNetDiskUtil.urlEncoder(path) +//上传后使用的文件绝对路径,需要urlencode,需要与上一个阶段预上传precreate接口中的path保持一致
"&uploadid=" + uploadid +//上一个阶段预上传precreate接口下发的uploadid
"&partseq=" + i; //文件分片的位置序号,从0开始,参考上一个阶段预上传precreate接口返回的block_list
int iTmp = i;
AtomicLong inc = new AtomicLong();
RandomAccessFileInputStream stream = new RandomAccessFileInputStream(raf, (int) (i * splitSize), (int) splitSize);
Map<String, Object> map = new HashMap<>();
map.put("file", new Resource() {
@Override
public String getName() {
return "ly_" + iTmp;
}
@Override
public URL getUrl() {
return null;
}
@Override
public InputStream getStream() {
return new InputStream() {
@Override
public int read() throws IOException {
int ret = stream.read();
size.addAndGet(1);
inc.addAndGet(1);
return ret;
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
int ret = stream.read(b, off, len);
size.addAndGet(ret);
inc.addAndGet(ret);
return ret;
}
};
}
});
Thread t = new Thread(() -> {
while (!Thread.interrupted()) {
try {
System.out.println("当前传输的大小为:" + size.get() + ",每秒传输大小:" + inc.getAndSet(0));
Thread.sleep(1000);
} catch (InterruptedException e) {
// e.printStackTrace();
break;
}
}
System.out.println("读取大小线程结束");
});
t.start();
/**
* 进行文件上传
*/
HttpUtil.post(url, map);
t.interrupt();
t.join();
}
return true;
} catch (Exception e) {
e.printStackTrace();
System.out.println("文件分片上传失败");
}
return false;
}
/**
* 分片上传
* -合并文件
*
* @param accessToken
* @param uploadid 预上传precreate接口下发的uploadid
* @param baiduUploadFileVo
*/
public static BaiduNetDiskMergeFile mergeFile(String accessToken, String uploadid, BaiduUploadFileVo baiduUploadFileVo) {
BaiduNetDiskMergeFile mergeFileDto = null;
try {
URL url = new URL(BaiduNetDiskConstant.BAIDUNETDISK_MERGE_FILE_URL +
"method=create" + //本接口固定为create
"&access_token=" + accessToken //接口鉴权认证参数,标识用户
);
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
httpConn.setRequestMethod("POST");
httpConn.setRequestProperty("User-Agent", "pan.baidu.com");
httpConn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
httpConn.setDoOutput(true);
OutputStreamWriter writer = new OutputStreamWriter(httpConn.getOutputStream());
writer.write("path=" + BaiduNetDiskUtil.urlEncoder(baiduUploadFileVo.getPath()) + //上传后使用的文件绝对路径,需要urlencode
"&size=" + baiduUploadFileVo.getSize() + //文件和目录两种情况:上传文件时,表示文件的大小,单位B;上传目录时,表示目录的大小,目录的话大小默认为0
"&isdir=" + baiduUploadFileVo.getIsdir() + //是否为目录,0 文件,1 目录
"&block_list=[\"" + baiduUploadFileVo.getBlockList() + "\"]" +//文件各分片MD5数组的json串
"&uploadid=" + uploadid + //预上传precreate接口下发的uploadid
"&rtype=3" //文件命名策略。1 表示当path冲突时,进行重命名 2 表示当path冲突且block_list不同时,进行重命名 3 当云端存在同名文件时,对该文件进行覆盖
);
writer.flush();
writer.close();
httpConn.getOutputStream().close();
int responseCode = httpConn.getResponseCode();
System.out.println("Response Code : " + responseCode);
BufferedReader in = new BufferedReader(new InputStreamReader(httpConn.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
log.info("分片上传-创建文件响应结果:{} " + response.toString());
mergeFileDto = JSON.parseObject(response.toString(), BaiduNetDiskMergeFile.class);
if (mergeFileDto == null || mergeFileDto.getErrno() != 0) {
System.out.println("合并文件失败");
}
return mergeFileDto;
} catch (Exception e) {
e.printStackTrace();
System.out.println("合并文件失败");
}
return null;
}
/**
* 获取文件的 md5
*
* @param obj
* @return
*/
public static BaiduUploadFileVo getFileMd5(Object obj) {
BaiduUploadFileVo uploadFileVo = new BaiduUploadFileVo();
try {
StringBuffer md5s = new StringBuffer();
File file = null;
if (obj instanceof String) {
file = new File((String) obj);
}
if (obj instanceof File) {
file = (File) obj;
}
long splitSize = BaiduNetDiskConstant.BAIDUNETDISK_SLICE_SIZE * 1024 * 1024;//单片文件大小
long fileSize = file.length();
uploadFileVo.setSize(fileSize);
/**
* 文件小于4m 无需切片
*/
if (fileSize < splitSize) {
//获取文件的md5
md5s.append(BigFileMD5Util.getMD5(file));
uploadFileVo.setBlockList(md5s.toString());
return uploadFileVo;
}
/**
* 对大于4m的文件进行切片
*/
RandomAccessFile raf = new RandomAccessFile(file, "r");
//要切多少片
int count = (int) (fileSize % splitSize != 0 ? fileSize / splitSize + 1 : fileSize / splitSize);
for (int i = 0; i < count; i++) {
RandomAccessFileInputStream randomAccessFileInputStream =
new RandomAccessFileInputStream(raf, i * splitSize, (int) splitSize);
//获取分片文件的md5
md5s.append(BigFileMD5Util.getMD5(randomAccessFileInputStream) + "\",\"");
}
String md5Str = md5s.toString();
md5s = new StringBuffer(md5Str.substring(0, md5Str.length() - 3));
uploadFileVo.setBlockList(md5s.toString());
return uploadFileVo;
} catch (Exception e) {
e.printStackTrace();
System.out.println("文件上传失败,获取分片文件MD5失败");
}
return null;
}
/**
* 单步上传
*
* @param accessToken
* @param path
* @param file
* @return
*/
public boolean singleUpload(String accessToken, String path, File file) {
try {
Map<String, Object> map = new HashMap<>();
String url = BaiduNetDiskConstant.BAIDUNETDISK_SINGLE_UPLOAD_URL +
"method=upload" + //本接口固定为upload
"&access_token=" + accessToken + //接口鉴权认证参数,标识用户
"&path=" + BaiduNetDiskUtil.urlEncoder(path) + //上传的文件绝对路径
"&ondup=newcopy"; //上传的文件绝对路径冲突时的策略。fail(默认:冲突时失败)overwrite(冲突时覆盖) newcopy(冲突时重命名)
map.put("file", file);
//上传文件
HttpUtil.post(url, map);
return true;
} catch (Exception e) {
e.printStackTrace();
System.out.println("文件上传失败");
}
return false;
}
}
package boot.test.dto;
import lombok.Data;
import java.util.List;
/**
* 百度网盘百度网盘分片上传之预上传
*/
@Data
public class BaiduNetDiskPrecreate extends BaiduRespondBase {
/**
* 文件的绝对路径
*/
private String path;
/**
* 上传唯一ID标识此上传任务
*/
private String uploadid;
/**
* 系统内部状态字段
*/
private Integer returnType;
/**
* 需要上传的分片序号列表,索引从0开始
*/
private List blockList;
}
package boot.test.dto;
import lombok.Data;
/**
* 百度网盘分片上传之合并文件
*/
@Data
public class BaiduNetDiskMergeFile extends BaiduRespondBase {
/**
* 文件在云端的唯一标识ID
*/
private Long fsId;
/**
* 文件的MD5,只有提交文件时才返回,提交目录时没有该值
*/
private String md5;
/**
* 文件名
*/
private String serverFilename;
/**
* 分类类型, 1 视频 2 音频 3 图片 4 文档 5 应用 6 其他 7 种子
*/
private Integer category;
/**
* 上传后使用的文件绝对路径
*/
private String path;
/**
* 文件大小,单位B
*/
private Long size;
/**
* 文件创建时间
*/
private Long ctime;
/**
* 文件修改时间
*/
private Long mtime;
/**
* 是否目录,0 文件、1 目录
*/
private Integer isdir;
public Long getCtime() {
if (ctime != null) {
return ctime * 1000;
}
return ctime;
}
public Long getMtime() {
if (mtime != null) {
return mtime * 1000;
}
return mtime;
}
}
package boot.test.vo;
import lombok.Data;
@Data
public class BaiduUploadFileVo {
private String path;
/**
* 文件和目录两种情况:上传文件时,表示文件的大小,单位B;上传目录时,表示目录的大小,目录的话大小默认为0
*/
private Long size;
private Integer isdir;
/**
* block_list的含义如下,如果上传的文件小于4MB,其md5值(32位小写)即为block_list字符串数组的唯一元素;
* 如果上传的文件大于4MB,需要将上传的文件按照4MB大小在本地切分成分片,不足4MB的分片自动成为最后一个分片,
* 所有分片的md5值(32位小写)组成的字符串数组即为block_list
*/
private String blockList;
}
测试
public static void main(String[] args) {
String accessToken = BaiduNetDiskConstant.ACCESSTOKEN;
String absoluteFilePath = "D:\\BaiduNetdiskDownload\\hhxi.zip";
/**
* 文件分片并获取md5值
*/
BaiduUploadFileVo baiduUploadFileVo = BaiduNetDiskUpload.getFileMd5(absoluteFilePath);
baiduUploadFileVo.setPath(BaiduNetDiskConstant.BAIDUNETDISK_APP_PATH + "hhxi.zip");
baiduUploadFileVo.setIsdir(0);
/**
* 预上传
*/
BaiduNetDiskPrecreate precreate = BaiduNetDiskUpload.precreate(accessToken, baiduUploadFileVo);
File f = new File(absoluteFilePath);
BaiduNetDiskUpload.shardUpload(accessToken, precreate.getUploadid(), baiduUploadFileVo.getPath(), f);
/**
* 创建文件
*/
BaiduNetDiskMergeFile mergeFileDto = BaiduNetDiskUpload.mergeFile(accessToken, precreate.getUploadid(), baiduUploadFileVo);
System.out.println(mergeFileDto.toString());
}
5. 文件下载(可获取进度)–方法一
package boot.test.baidu;
import boot.test.constant.BaiduNetDiskConstant;
import boot.test.dto.BaiduNetDiskMergeFile;
import boot.test.dto.BaiduNetDiskPrecreate;
import boot.test.utils.BaiduNetDiskUtil;
import boot.test.vo.BaiduUploadFileVo;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.StreamProgress;
import cn.hutool.core.io.resource.Resource;
import cn.hutool.http.Header;
import cn.hutool.http.HttpException;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicLong;
/**
* 百度网盘文件下载类
*/
@Slf4j
public class BaiduNetDiskDownload {
/**
* 查询文件信息
*
* @param accessToken
* @param fileIds 文件id数组
*/
public static List<BaiduNetDiskFileInfo> getFileInfos(String accessToken, Long[] fileIds) {
BufferedReader in = null;
try {
URL url = new URL(BaiduNetDiskConstant.BAIDUNETDISK_MULTIMEDIA_URL +
"method=filemetas" + //本接口固定为filemetas
"&access_token=" + accessToken +//接口鉴权参数
"&fsids=" + BaiduNetDiskUtil.urlEncoder(Arrays.toString(fileIds)) +//文件id数组,数组中元素是uint64类型,数组大小上限是:100
"&thumb=1" +//是否需要缩略图地址,0为否,1为是,默认为0
"&dlink=1" +//是否需要下载地址,0为否,1为是,默认为0。获取到dlink后,参考下载文档进行下载操作
"&extra=1" +//图片是否需要拍摄时间、原图分辨率等其他信息,0 否、1 是,默认0
"&needmedia=1" +//视频是否需要展示时长信息 0 否、1 是,默认0
"&detail=1" //频是否需要展示长,宽等信息。0 否、1 是,默认0
);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
log.info("获取百度网盘文件信息响应结果:{} " + response.toString());
JSONObject responseJson = JSON.parseObject(response.toString());
if (responseJson.getInteger("errno") != 0) {
System.out.println("获取文件信息失败");
}
return JSONObject.parseArray(responseJson.getString("list"), BaiduNetDiskFileInfo.class);
} catch (Exception e) {
e.printStackTrace();
System.out.println("获取文件信息失败");
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e2) {
e2.printStackTrace();
}
}
}
return null;
}
/**
* 文件下载
*
* @param dLink dlink字段即为文件的下载地址 通过【查询文件信息接口】获取
* @param accessToken
* @return
*/
public static boolean downLoad(String dLink, String fileName, String accessToken) {
try {
URL url = new URL(dLink + "&access_token=" + accessToken);
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
httpConn.setRequestMethod("GET");
httpConn.setRequestProperty("User-Agent", "pan.baidu.com");
httpConn.setRequestProperty("Host", "d.pcs.baidu.com");
int responseCode = httpConn.getResponseCode();
if (responseCode == 302) {
final long[] downloadedSize = {0};
String location = httpConn.getHeaderField("Location");
HttpUtil.downloadFile(location, FileUtil.file("D:\\" + fileName), new StreamProgress() {
@Override
public void start() {
System.out.println("开始下载。。。。");
}
@Override
public void progress(long progressSize) {
// FileUtil.readableFileSize(progressSize) 将字节转为M或G
System.out.println("当前传输的大小为:" + FileUtil.readableFileSize(progressSize) + ", 每秒传输大小:" + FileUtil.readableFileSize(progressSize - downloadedSize[0]));
downloadedSize[0] = progressSize;
}
@Override
public void finish() {
System.out.println("下载完成!");
}
});
}
log.info("文件下载完成");
} catch (Exception e) {
e.printStackTrace();
System.out.println("文件下载失败");
}
return false;
}
}
package boot.test.dto;
import lombok.Data;
/**
* 百度网盘 查询文件信息
*/
@Data
public class BaiduNetDiskFileInfo {
/**
* 文件类型,含义如下:1 视频, 2 音乐,3 图片,4 文档,5 应用,6 其他,7 种子
*/
private Integer category;
/**
* 文件下载地址,参考下载文档进行下载操作
*/
private String dlink;
/**
* 文件名
*/
private String filename;
/**
* 是否是目录,为1表示目录,为0表示非目录
*/
private Integer isdir;
/**
* 文件的服务器创建Unix时间戳,单位秒
*/
private Long serverCtime;
/**
* 文件的服务器修改Unix时间戳,单位秒
*/
private Long serverMtime;
/**
* 文件大小,单位字节
*/
private Long size;
/**
* 缩略图地址
*/
private String thumbs;
/**
* 图片高度
*/
private Integer height;
/**
* 图片宽度
*/
private Integer width;
/**
* 图片拍摄时间
*/
private Long dateTaken;
/**
* 图片旋转方向信息
*/
private String orientation;
public Long getServerCtime() {
if (serverCtime != null) {
return serverCtime * 1000;
}
return serverCtime;
}
public Long getServerMtime() {
if (serverMtime != null) {
return serverMtime * 1000;
}
return serverMtime;
}
public Long getDateTaken() {
if (dateTaken != null) {
return dateTaken * 1000;
}
return dateTaken;
}
}
测试
public static void main(String[] args) {
String accessToken = BaiduNetDiskConstant.ACCESSTOKEN;
/**
* 获取文件列表
*/
BaiduFileListVo baiduFileListVo = new BaiduFileListVo();
baiduFileListVo.setDir(BaiduNetDiskConstant.BAIDUNETDISK_APP_PATH);
List<BaiduNetDiskFileList> baiduFileListByPath = getFileList(baiduFileListVo, accessToken);
System.out.println(baiduFileListByPath.toString());
/**
* 获取文件信息
*/
Long[] ids = {1076025667018234l, 415956271116370l};//👆👆👆👆👆👆👆👆从文件列表中获取👆👆👆👆👆👆👆👆👆👆
List<BaiduNetDiskFileInfo> fileInfos = getFileInfos(accessToken, ids);
for (BaiduNetDiskFileInfo fileInfo : fileInfos) {
System.out.println(fileInfo.toString());
}
/**
* 文件下载
*/
BaiduNetDiskFileInfo baiduNetDiskFileInfo = fileInfos.get(0);
boolean b = BaiduNetDiskUpload.downLoad(baiduNetDiskFileInfo.getDlink(), baiduNetDiskFileInfo.getFilename(), accessToken);
}
6. 获取文件列表
package boot.test.baidu;
import boot.test.constant.BaiduNetDiskConstant;
import boot.test.dto.*;
import boot.test.utils.BaiduNetDiskUtil;
import boot.test.vo.BaiduFileListVo;
import boot.test.vo.BaiduUploadFileVo;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Arrays;
import java.util.List;
@Slf4j
public class BaiduNetDiskFileManager {
/**
* 获取用户网盘中的文件列表
*
* @param baiduFileListVo
* @param accessToken
*/
public static List<BaiduNetDiskFileList> getFileList(BaiduFileListVo baiduFileListVo, String accessToken) {
BufferedReader in = null;
try {
URL url = new URL(BaiduNetDiskConstant.BAIDUNETDISK_FILE_LIST_URL +
"method=list" + //本接口固定为list
"&dir=" + BaiduNetDiskUtil.urlEncoder(baiduFileListVo.getDir()) + //需要list的目录,以/开头的绝对路径, 默认为/ 路径包含中文时需要UrlEncode编码
"&access_token=" + accessToken + //接口鉴权参数
"&web=web" + //值为1时,返回dir_empty属性和缩略图数据
"&folder=0" + //是否只返回文件夹,0 返回所有,1 只返回文件夹,且属性只返回path字段
"&order=time" //排序字段:默认为name;name表示先按文件类型排序,后按文件名称排序;
// "&desc=1" + //默认为升序,设置为1实现降序 (注:排序的对象是当前目录下所有文件,不是当前分页下的文件)
// "&start=0" + //起始位置,从0开始
// "&limit=10" + //查询数目,默认为1000,建议最大不超过1000
);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
log.info("获取百度网盘中的文件列表响应结果:{} " + response.toString());
JSONObject resultJson = JSON.parseObject(response.toString());
if (resultJson.getInteger("errno") != 0) {
System.out.println("获取百度网盘文件列表失败");
}
return JSONObject.parseArray(resultJson.getString("list"), BaiduNetDiskFileList.class);
} catch (Exception e) {
e.printStackTrace();
System.out.println("获取百度网盘文件列表失败");
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e2) {
e2.printStackTrace();
}
}
}
return null;
}
/**
* 获取百度网盘文档列表
*
* @param path
* @param accessToken
* @return
*/
public List<BaiduNetDiskDocList> getDoclist(String path, String accessToken) {
BufferedReader in = null;
try {
URL url = new URL(BaiduNetDiskConstant.BAIDUNETDISK_FILE_LIST_URL +
"method=doclist" + //本接口固定为doclist
"&parent_path=" + BaiduNetDiskUtil.urlEncoder(path) + //目录名称,以/开头的绝对路径, 默认为/ 路径包含中文时需要UrlEncode编码
"&access_token=" + accessToken + //接口鉴权参数
"&web=1" //为1时返回文档预览地址lodocpreview
// "&page=1" + //页码,从1开始, 如果不指定页码,则为不分页模式,返回所有的结果。如果指定page参数,则按修改时间倒序排列
// "&num=5" //一页返回的文档数, 默认值为1000,建议最大值不超过1000
);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
log.info("获取百度网盘文档列表响应结果:{} " + response.toString());
JSONObject responseJson = JSON.parseObject(response.toString());
if (responseJson.getInteger("errno") != 0) {
System.out.println("获取百度网盘文档列表失败");
}
return JSONObject.parseArray(responseJson.getString("info"), BaiduNetDiskDocList.class);
} catch (Exception e) {
e.printStackTrace();
System.out.println("获取百度网盘文档列表失败");
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e2) {
e2.printStackTrace();
}
}
}
return null;
}
}
package boot.test.dto;
import lombok.Data;
/**
* 百度网盘文件列表
*/
@Data
public class BaiduNetDiskFileList {
/**
* 文件在云端的唯一标识ID
*/
private Long fsId;
/**
* 文件的绝对路径
*/
private String path;
/**
* 文件名称
*/
private String serverFilename;
/**
* 文件大小,单位B
*/
private Long size;
/**
* 文件在服务器修改时间
*/
private Long serverMtime;
/**
* 文件在服务器创建时间
*/
private Long serverCtime;
/**
* 文件在客户端修改时间
*/
private Long localMtime;
/**
* 文件在客户端创建时间
*/
private Long localCtime;
/**
* 是否为目录,0 文件、1 目录
*/
private Integer isdir;
/**
* 文件类型,1 视频、2 音频、3 图片、4 文档、5 应用、6 其他、7 种子
*/
private Integer category;
/**
* 云端哈希(非文件真实MD5),只有是文件类型时,该字段才存在
*/
private String md5;
/**
* 该目录是否存在子目录,只有请求参数web=1且该条目为目录时,该字段才存在, 0为存在, 1为不存在
*/
private Integer dirEmpty;
/**
* 只有请求参数web=1且该条目分类为图片时,该字段才存在,包含三个尺寸的缩略图URL
*/
private String thumbs;
public Long getServerCtime() {
if (serverCtime != null) {
return serverCtime * 1000;
}
return serverCtime;
}
public Long getServerMtime() {
if (serverMtime != null) {
return serverMtime * 1000;
}
return serverMtime;
}
public Long getLocalMtime() {
if (localMtime != null) {
return localMtime * 1000;
}
return localMtime;
}
public Long getLocalCtime() {
if (localCtime != null) {
return localCtime * 1000;
}
return localCtime;
}
}
package boot.test.dto;
import lombok.Data;
/**
* 用户网盘中文档列表
*/
@Data
public class BaiduNetDiskDocList {
/**
* 文件在云端的唯一标识
*/
private Long fsId;
/**
* 文件名
*/
private String serverFilename;
/**
* 文件路径
*/
private String path;
/**
* 文件类型
*/
private Integer category;
/**
* 文件大小
*/
private Long size;
/**
* 是否是目录,0为否,1为是
*/
private Integer isdir;
/**
* 文件在客户端创建时间
*/
private Long localCtime;
/**
* 文件在客户端修改时间
*/
private Long localMtime;
/**
* 文件在服务端创建时间
*/
private Long serverCtime;
/**
* 文件在服务端修改时间
*/
private Long serverMtime;
/**
* 云端哈希(非文件真实MD5)
*/
private String md5;
private String objectKey;
private Integer share;
// private String lodocpreview;
// private String docpreview;
public Long getServerCtime() {
if (serverCtime != null) {
return serverCtime * 1000;
}
return serverCtime;
}
public Long getServerMtime() {
if (serverMtime != null) {
return serverMtime * 1000;
}
return serverMtime;
}
public Long getLocalMtime() {
if (localMtime != null) {
return localMtime * 1000;
}
return localMtime;
}
public Long getLocalCtime() {
if (localCtime != null) {
return localCtime * 1000;
}
return localCtime;
}
}
测试
public static void main(String[] args) {
String accessToken = BaiduNetDiskConstant.ACCESSTOKEN;
/**
* 获取文件列表
*/
BaiduFileListVo baiduFileListVo = new BaiduFileListVo();
baiduFileListVo.setDir(BaiduNetDiskConstant.BAIDUNETDISK_APP_PATH);
List<BaiduNetDiskFileList> baiduFileListByPath = getFileList(baiduFileListVo, accessToken);
System.out.println(baiduFileListByPath.toString());
}