Java 对接百度网盘

文章目录

  • 前言
  • 一、创建百度网盘账号
  • 二、代码实现
    • 1. 常量类
    • 2. 工具类
    • 3. 授权码模式授权
    • 4. 文件分片上传(可获取进度)--方法一
    • 5. 文件下载(可获取进度)--方法一
    • 6. 获取文件列表
    • 7. 文件分片上传(不可获取进度)--方法二
    • 7. 文件下载(不可获取进度)--方法二


前言

百度网盘官方文档中的介绍的很清楚明了,在此不做赘述,直接上代码!!!

提示:以下是案例是基于企业认证的账号

一、创建百度网盘账号

百度网盘官方文档

  1. 进行百度帐号的实名认证;
  2. 认证成功后,创建应用; 在【控制台】中点击「+创建应用」创建一个应用
  3. 完成应用创建后,你的应用可以获取到关键凭证:Appid、Appkey、Secretkeyk和Signkey。
    在这里插入图片描述
  4. 设置授权的回调地址,如果不设置回调地址那么使用默认的回调地址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());
    }

7. 文件分片上传(不可获取进度)–方法二

7. 文件下载(不可获取进度)–方法二

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/643713.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

算法之堆排序

堆排序是一种基于比较的排序算法&#xff0c;通过构建二叉堆&#xff08;Binary Heap&#xff09;&#xff0c;可以利用堆的性质进行高效的排序。二叉堆是一个完全二叉树&#xff0c;可以有最大堆和最小堆两种形式。在最大堆中&#xff0c;父节点的值总是大于或等于其子节点的值…

C++与Android处理16进制大端/小端数据实例(二百七十六)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

“大数据建模、分析、挖掘技术应用研修班”的通知!

随着2015年9月国务院发布了《关于印发促进大数据发展行动纲要的通知》&#xff0c;各类型数据呈现出了指数级增长&#xff0c;数据成了每个组织的命脉。今天所产生的数据比过去几年所产生的数据大好几个数量级&#xff0c;企业有了能够轻松访问和分析数据以提高性能的新机会&am…

夏日采摘季,视频智能监控管理方案助力智慧果园管理新体验

5月正值我国各地西瓜、杨梅、大樱桃、油桃等水果丰收的季节&#xff0c;许多地方都举办了采摘旅游活动&#xff0c;吸引了众多游客前来体验采摘乐趣。随着采摘的人流量增多&#xff0c;果园的管理工作也面临压力。 为了提升水果园采摘活动的管理效果&#xff0c;减少人工巡查成…

harbor 认证

Harbor 认证过程 Harbor以 Docker Registry v2认证为基础&#xff0c;添加上一层权限保护。1.v2 集成了一个安全认证的功能&#xff0c;将安全认证暴露给外部服务&#xff0c;让外部服务去实现2.强制用户每次Docker pull/push请求都要带一个合法的Token&#xff0c;Registry会…

基于jeecgboot-vue3的Flowable新建流程定义(一)

因为这个项目license问题无法开源&#xff0c;更多技术支持与服务请加入我的知识星球。 1、vue3版本因为流程分类是动态的&#xff0c;不再固定了&#xff0c;所以新建的时候需要选择建立哪种流程类型的流程 代码如下&#xff1a; <!-- 选择模型的流程类型对话框 -->&…

JDBCTemplate介绍

Spring JDBC Spring框架对Spring的简单封装。提供一个JDBCTemplate对象简化JDBC开发 *步骤&#xff1a; 1、导入jar包 2、创建JDBCTemplate对象。依赖于数据源DataSource *JdbcTemplate templatenew JdbcTemplate(ds); 3、调用JdbcTemplate的方法来完成CRUD的操作 *update()&…

【实战教程】使用Spring AOP和自定义注解监控接口调用

一、背景 随着项目的长期运行和迭代&#xff0c;积累的功能日益繁多&#xff0c;但并非所有功能都能得到用户的频繁使用或实际上根本无人问津。 为了提高系统性能和代码质量&#xff0c;我们往往需要对那些不常用的功能进行下线处理。 那么&#xff0c;该下线哪些功能呢&…

代码随想录-Day18

513. 找树左下角的值 给定一个二叉树的 根节点 root&#xff0c;请找出该二叉树的 最底层 最左边 节点的值。 假设二叉树中至少有一个节点。 方法一&#xff1a;深度优先搜索 class Solution {int curVal 0;int curHeight 0;public int findBottomLeftValue(TreeNode roo…

GitLens或者Git Graph在vscode中对比文件历史变化,并将历史变化同步到当前文件中

有时候我们上周改的代码&#xff0c;现在想反悔把它恢复过来&#xff0c;怎么办&#xff1f;&#xff1f;&#xff1f;很好&#xff0c;你有这个需求&#xff0c;说明你找对人了&#xff0c;那就是我们需要在vscode中安装这个插件&#xff1a;GitLens或者Git Graph&#xff0c;…

做抖店四年来的经验分享,想做抖店的多看看,给你揭露真正的抖店

大家好&#xff0c;我是电商花花。 我做抖音小店从21年就已经开始了&#xff0c;中间一直都没断过&#xff0c;一直都抖店无货源&#xff0c;从刚开始的一家店铺&#xff0c;到现在的80多家店铺&#xff0c;不断完善和总结我们做店的方法。 在我看来做抖音小店现在很简单&…

Linux服务升级:Twemproxy 升级 Redis代理

目录 一、实验 1.环境 2.多实例Redis部署 3.Twemproxy 升级Redis代理 一、实验 1.环境 &#xff08;1&#xff09;主机 表1 主机 系统版本软件IP备注CentOS7.9Twemproxy192.168.204.200 Redis代理 Redis127.0.0.1:6379第一个Redis实例 Redis127.0.0.1:6380第二个…

别被“涨价“带跑,性价比才是消费真理

文章来源&#xff1a;全食在线 “再不好好赚钱&#xff0c;连方便面也吃不起了。”这是昨天在热搜下&#xff0c;一位网友的留言。而热搜的内容&#xff0c;正是康师傅方便面即将涨价的消息。 01 传闻初现 昨天上午&#xff0c;朋友圈就有人放出康师傅方便面要涨价的消息&am…

Py之llama-parse:llama-parse(高效解析和表示文件)的简介、安装和使用方法、案例应用之详细攻略

Py之llama-parse&#xff1a;llama-parse(高效解析和表示文件)的简介、安装和使用方法、案例应用之详细攻略 目录 llama-parse的简介 llama-parse的安装和使用方法 1、安装 2、使用方法 第一步&#xff0c;获取API 密钥 第二步&#xff0c;安装LlamaIndex、LlamaParse L…

从ZooKeeper切换到ClickHouse-Keeper,藏着怎样的秘密

本文字数&#xff1a;7772&#xff1b;估计阅读时间&#xff1a;20 分钟 作者&#xff1a;博睿数据 李骅宸&#xff08;太道&#xff09;& 小叮当 本文在公众号【ClickHouseInc】首发 本系列前两篇内容&#xff1a; 从ES到ClickHouse&#xff0c;Bonree ONE平台更轻更快&a…

API攻击呈指数级增长,如何保障API安全?

从远程医疗、共享汽车到在线银行&#xff0c;实时API是构建数字业务的基础。然而&#xff0c;目前超过90%的基于Web的网络攻击都以API端点为目标&#xff0c;试图利用更新且较少为人所知的漏洞&#xff0c;而这些漏洞通常是由安全团队未主动监控的API所暴露&#xff0c;致使API…

【设计模式】JAVA Design Patterns——Callback(回调模式)

&#x1f50d;目的 回调是一部分被当为参数来传递给其他代码的可执行代码&#xff0c;接收方的代码可以在一些方便的时候来调用它。 &#x1f50d;解释 真实世界例子 我们需要被通知当执行的任务结束时。我们为调用者传递一个回调方法然后等它调用通知我们。 通俗描述 回调是一…

K8s 部署prometheus

文章目录 K8s 部署prometheuskube-prometheus 部署部署流程安装卸载补充 K8s 部署prometheus kube-prometheus 部署 kube-prometheus 是 github 上开源的整合了 prometheus alertmanager granfana 等监控工具的项目&#xff0c;github地址 如果github 访问不了的也可以选择 g…

忘记“也是一门学问:机器如何忘记自己学到的知识?

在信息时代&#xff0c;我们常常希望人工智能能够学到更多的知识&#xff0c;变得更加智能。但你是否想过&#xff0c;有时候让机器"忘记"一些它学到的东西&#xff0c;也是一件很重要的事&#xff1f; 随着用户隐私保护意识的提高和相关法律法规的出台&#xff0c;…

张大哥笔记:穷人都在拼命挣钱,而富人都在努力让自己更值钱

最近行业大佬&#xff0c;纷纷网红化&#xff0c;比如周鸿祎&#xff0c;雷军&#xff0c;刘强东纷纷下场&#xff01; 大佬当网红&#xff0c;图啥&#xff1f;当然是图钱了。 大佬都很精的&#xff0c;他们老早就运用媒体的传播杠杆&#xff0c;把自己热度炒起来。 在不断…