0
点赞
收藏
分享

微信扫一扫

SpringBoot业务开发 01、Springboot实战:实现Gitee图床上传及删除(含完整代码)



文章目录

  • ​​引言​​
  • ​​一、实际测试及配置​​
  • ​​1.1、接口测试​​
  • ​​1.2、项目配置及使用​​
  • ​​二、Gitee的接口API​​
  • ​​三、实现说明​​
  • ​​3.1、依赖版本​​
  • ​​3.2、项目结构​​
  • ​​3.3、代码实现​​
  • ​​config​​
  • ​​controller​​
  • ​​domain​​
  • ​​Exception​​
  • ​​handler​​
  • ​​utils​​
  • ​​Application.yaml​​
  • ​​参考资料​​

引言

个人项目中由于要对图片进行存储,想到做笔记图片都是存储到图床上所以就想调用Gitee的接口来实现,最后方案还是因为网络延时太大没有选择图床存储,而是直接存储到至本地服务器。

在这里给出上传Gitee图床案例demo的项目地址及使用说明,若是好用请给个star:

  • Github地址:https://github.com/changlua/CL-SpringbootDemos,对应仓库项目文件​​01、springboot-Gitee​


一、实际测试及配置

1.1、接口测试


测试工具Api-Post


包含两个接口:上传图片、删除图片。

上传图片:POST请求,body请求体使用​​multpart/form-data​​格式,图片文件名称为file。响应result结果为资源下载路径。

SpringBoot业务开发 01、Springboot实战:实现Gitee图床上传及删除(含完整代码)_java

删除图片:POST请求,body使用​​application/json​​格式,携带url参数为待删除的图片地址。

SpringBoot业务开发 01、Springboot实战:实现Gitee图床上传及删除(含完整代码)_spring boot_02



1.2、项目配置及使用


核心修改配置点


只需要关注以下配置信息即可:

SpringBoot业务开发 01、Springboot实战:实现Gitee图床上传及删除(含完整代码)_git_03

配置文件中配置的文件路径可覆盖Util中的文件路径,格式为xxx/形式

SpringBoot业务开发 01、Springboot实战:实现Gitee图床上传及删除(含完整代码)_json_04

上面的配置好之后即可启动项目,默认端口为8081,对应的两个接口写在​​controller/GiteeController.java​​中:

SpringBoot业务开发 01、Springboot实战:实现Gitee图床上传及删除(含完整代码)_spring boot_05




配置点说明


我们需要提前准备好请求地址中的​​owner、repo​​​以及请求体中​​access_token​​:

  • ​owner·​​:指的是个人空间,如下我的空间地址。
  • SpringBoot业务开发 01、Springboot实战:实现Gitee图床上传及删除(含完整代码)_json_06
  • ​repo​​:指的是你要上传的仓库名称,如下图:
  • SpringBoot业务开发 01、Springboot实战:实现Gitee图床上传及删除(含完整代码)_spring boot_07
  • ​access_token​​:用来表示你身份的一串字符串,通过该token就能够确定你的身份!该token生成时只会显示一次,妥善保存好,我的已经生成好了:
  • SpringBoot业务开发 01、Springboot实战:实现Gitee图床上传及删除(含完整代码)_spring boot_08

关于目录:

SpringBoot业务开发 01、Springboot实战:实现Gitee图床上传及删除(含完整代码)_java_09



二、Gitee的接口API

Gitee为开发者也提供了API,见:​​Gitee—API文档​​

对于上传图床我们只需要关注新建文件部分:请求url中​​{xxx}​​​是我们需要自己填充的部分,下面​​Response Class​​​是向gitee发出请求后得到的响应实体类结构,得到响应内容中我们关心的就是​​download_url​​属性,其是可供我们下载的图片地址。

SpringBoot业务开发 01、Springboot实战:实现Gitee图床上传及删除(含完整代码)_json_10

SpringBoot业务开发 01、Springboot实战:实现Gitee图床上传及删除(含完整代码)_json_11

下面是需要携带的请求体参数(封装到body中):​​access_token​​是每次请求必带的,其他的根据指定的接口携带

SpringBoot业务开发 01、Springboot实战:实现Gitee图床上传及删除(含完整代码)_spring boot_12

​上传文件接口​​​:POST请求,直接发送请求上传即可。请求体携带​​access_token​​​、​​content​​(文件内容经过base64编码);最后在请求成功后响应对象里属性名为download_url的就是下载地址。

​删除文件接口​​:①GET请求,调用获取仓库具体路径内容接口,取到sha属性。②DELETE请求,携带sha、access_token等其他请求体参数来调用删除接口。

获取sha的接口如下:

SpringBoot业务开发 01、Springboot实战:实现Gitee图床上传及删除(含完整代码)_java_13



三、实现说明

3.1、依赖版本

​spring-boot-starter-web​​:2.6.1

​hutool-all​​:5.5.8

​lombok​​:1.18.20

<!--    额外引入工具类    -->
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<!-- Hutool工具类 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.10</version>
</dependency>

本项目里使用hutool中的​​HttpUtil​​来发起请求调用。



3.2、项目结构

SpringBoot业务开发 01、Springboot实战:实现Gitee图床上传及删除(含完整代码)_spring boot_14

3.3、代码实现

config

定义一些调用Gitee的API响应属性:

/**
* @ClassName Constant
* @Author ChangLu
* @Date 2021/12/11 0:36
* @Description Gitee相关常量
*/
public class GiteeConstant {

public static String RESULT_BODY_COMMIT = "commit";

public static String RESULT_BODY_CONTENT = "content";

public static String RESULT_BODY_DOWNLOAD_URL = "download_url";

public static String RESULT_BODY_SHA = "sha";

}



controller

控制器:包含封装好的上传、删除接口

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.changlu.springboot.Exception.CommonEnum;
import com.changlu.springboot.Exception.OwnException;
import com.changlu.springboot.config.GiteeConstant;
import com.changlu.springboot.domain.Basic.ResultBody;
import com.changlu.springboot.domain.Request.BasicRequest;
import com.changlu.springboot.utils.GiteeImgBedUtil;
import com.changlu.springboot.utils.WebTools;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;

/**
* @ClassName GiteeController
* @Author ChangLu
* @Date 2021/12/1 0:05
* @Description Gitee控制器
*/

@RestController
@Slf4j
public class GiteeController {

@Value("${gitee.upload.path}")
private String MEMBERS_UPLOAD_PATH;

/**
* 上传文件
*
* @param multipartFile 文件对象
* @return
* @throws IOException
*/
@PostMapping("/gitee/upload")
public ResultBody uploadFile(@RequestParam("file") MultipartFile multipartFile) throws IOException {
log.info("uploadFile()请求已来临...");
//根据文件名生成指定的请求url
String originalFilename = multipartFile.getOriginalFilename();
if (originalFilename == null) {
log.info("服务器接收文件失败!");
throw new OwnException(CommonEnum.IMAGE_EXIST_ERROR);
}
//Gitee请求:发送上传文件请求
String JSONResult = GiteeImgBedUtil.uploadFile(MEMBERS_UPLOAD_PATH, originalFilename, multipartFile.getBytes());
//解析响应JSON字符串
JSONObject jsonObj = JSONUtil.parseObj(JSONResult);
//请求失败
if (jsonObj == null || jsonObj.getObj(GiteeConstant.RESULT_BODY_COMMIT) == null) {
log.info("上传文件失败!");
return ResultBody.fail(CommonEnum.IMAGE_UPLOAD_ERROR);
}
//请求成功:返回下载地址
JSONObject content = JSONUtil.parseObj(jsonObj.getObj(GiteeConstant.RESULT_BODY_CONTENT));
log.info("上传成功,下载地址为:" + content.getObj(GiteeConstant.RESULT_BODY_DOWNLOAD_URL));
return ResultBody.success(content.getObj(GiteeConstant.RESULT_BODY_DOWNLOAD_URL));
}

/**
* 删除文件
*
* @param request
* @return
*/
@PostMapping("/gitee/del")
public ResultBody delFile(@RequestBody BasicRequest request) {
//1、解析取得原始上传路径
String url = request.getUrl();
if (WebTools.isNotEmpty(url) && !url.contains("master/")) {
log.info("url:" + url + " 无法解析路径!");
throw new OwnException(CommonEnum.URL_PARSE_FAILED);
}
String path = url.substring(url.indexOf("master/") + 7);
log.info("解析取得待删除路径:" + path);
if (!WebTools.isEmpty(path)) {
//2、Gitee请求:获取sha
String shaResult = GiteeImgBedUtil.getSha(path);
JSONObject jsonObj = JSONUtil.parseObj(shaResult);
if (jsonObj == null) {
log.info("delFile中获取sha失败!");
return ResultBody.fail(CommonEnum.DEL_FILE_FAILED);
}
String sha = jsonObj.getStr(GiteeConstant.RESULT_BODY_SHA);
//3、Gitee请求:发送删除请求
String JSONResult = GiteeImgBedUtil.deleteFile(path, sha);
jsonObj = JSONUtil.parseObj(JSONResult);
if (jsonObj == null || jsonObj.getObj(GiteeConstant.RESULT_BODY_COMMIT) == null) {
log.info("删除文件失败!");
return ResultBody.fail(CommonEnum.DEL_FILE_FAILED);
}
}
log.info("文件路径为:" + path + " 删除成功!");
return ResultBody.success("删除成功!");
}

}



domain


Basic


​BaseExceptionInfoInterface.java​​:异常信息接口,之后枚举类会实现该接口

/**
* 基本异常接口
* @author changlu
* @date 2021/07/22 17:03
**/
public interface BaseExceptionInfoInterface {

/**
* 得到错误码
* @date 2021/07/22 17:04
* @return java.lang.String
*/
Integer getResultCode();

/**
* 得到错误信息
* @date 2021/07/22 17:05
* @return java.lang.String
*/
String getResultMsg();

}

​ResultBody.java​​:响应体封装,统一接口返回对象

import com.changlu.springboot.Exception.CommonEnum;
import lombok.Data;

/**
* @ClassName ResultBody
* @Author ChangLu
* @Date 2021/9/21 9:55
* @Description 响应体封装
*/
@Data
public class ResultBody {

/**
* 响应码
*/
private Integer code;
/**
* 响应消息
*/
private String message;

/**
* 响应结果
*/
private Object result;

public ResultBody() {
}

/**
* 内部封装
* @param code
* @param message
*/
private ResultBody(Integer code, String message) {
this.code = code;
this.message = message;
}

/**
* 响应码与响应结果封装
*/
public ResultBody(BaseExceptionInfoInterface baseErrorInfoInterface) {
this.code = baseErrorInfoInterface.getResultCode();
this.message = baseErrorInfoInterface.getResultMsg();
}

/**
* 成功
* @param data 数据
* @return ResultBody
*/
public static ResultBody success(Object data){
ResultBody resultBody = new ResultBody(CommonEnum.SUCCESS);
resultBody.setResult(data);
return resultBody;
}

/**
* 错误
* @param baseErrorInfoInterface 枚举类
* @return ResultBody
*/
public static ResultBody fail(BaseExceptionInfoInterface baseErrorInfoInterface){
ResultBody resultBody = new ResultBody(baseErrorInfoInterface);
resultBody.setResult(null);
return resultBody;
}

/**
* 可自定义错误描述,
* @param baseErrorInfoInterface
* @param errMsg
* @return
*/
public static ResultBody fail(BaseExceptionInfoInterface baseErrorInfoInterface,String errMsg){
ResultBody resultBody = fail(baseErrorInfoInterface);
resultBody.setMessage(errMsg);
return resultBody;
}


/**
* 错误
* @param code 状态码
* @param message 描述信息
* @return ResultBody
*/
public static ResultBody fail(Integer code,String message){
ResultBody resultBody = new ResultBody(code,message);
resultBody.setResult(null);
return resultBody;
}


}




BasicRequest


用于取得对象体中的url属性,应用在删除接口中:

import lombok.Data;

import java.io.Serializable;

/**
* @ClassName BasicRequest
* @Author ChangLu
* @Date 2021/12/10 23:53
* @Description 请求对象封装
*/
@Data
public class BasicRequest implements Serializable {

private static final long serialVersionUID = 1L;

private String url;

}



Exception

​CommonEnum.java​​:响应状态码及描述枚举类

import com.changlu.springboot.domain.Basic.BaseExceptionInfoInterface;

/**
* 常见异常枚举类
* @author changlu
* @date 2021/07/22 17:05
**/
public enum CommonEnum implements BaseExceptionInfoInterface {

//成功情况
SUCCESS(200,"成功!"),
//图床异常处理
IMAGE_EXIST_ERROR(1001, "服务器未接收到上传资源"),
IMAGE_UPLOAD_ERROR(1002,"上传Gitee图床失败"),
DEL_FILE_FAILED(1003,"图片删除失败"),
URL_PARSE_FAILED(1004,"Gitee图片url无法解析"),

//系统异常
SYSTEM_ERROR(2000, "系统异常,请从控制台或日志中查看具体错误信息");

//错误码
private final Integer resultCode;

//描述信息
private final String resultMsg;

CommonEnum(Integer resultCode, String resultMsg) {
this.resultCode = resultCode;
this.resultMsg = resultMsg;
}

@Override
public Integer getResultCode() {
return resultCode;
}

@Override
public String getResultMsg() {
return resultMsg;
}
}

​OwnException.java​​:自定义异常类,用于抛出一些自定义的异常信息以及用于全局异常处理器捕获

/**
* @ClassName OwnException
* @Author ChangLu
* @Date 2021/7/29 23:40
* @Description 自定义异常类
*/
public class OwnException extends RuntimeException{

private final Integer code;
private final String message;

public OwnException(Integer code, String message){
this.code = code;
this.message = message;
}

public OwnException(CommonEnum ex){
this(ex.getResultCode(),ex.getResultMsg());
}

public OwnException(CommonEnum ex, String msg){
this(ex.getResultCode(),msg);
}

public Integer getCode() {
return code;
}

@Override
public String getMessage() {
return message;
}
}



handler

​GlobalExceptionHandler.java​​:全局异常处理器,这里对于Exception以及自定义异常进行异常捕捉

import com.changlu.springboot.Exception.CommonEnum;
import com.changlu.springboot.Exception.OwnException;
import com.changlu.springboot.domain.Basic.ResultBody;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletRequest;

/**
* @ClassName GlobalExceptionHandler
* @Author ChangLu
* @Date 2021/12/11 0:04
* @Description 全局异常捕获器
*/
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

/**
* 处理全局异常(Exception)
* @param e
* @return
*/
@ExceptionHandler(Exception.class)
public ResultBody handleException(HttpServletRequest request, Exception e){
log.error("Exception:",e);
return ResultBody.fail(CommonEnum.SYSTEM_ERROR);
}

/**
* 自定义异常(可自行抛出针对于一些受检类型,继承RuntimeException)
* @param ex 自定义抛出异常
* @return xyz.changlu.util.ResultBody
*/
@ExceptionHandler(value = OwnException.class)
public ResultBody msgExceptionHandler(HttpServletRequest request, OwnException ex){
log.error("OwnException:",ex);
return ResultBody.fail(ex.getCode(),ex.getMessage());
}

}



utils

​FileUtil.java​​:文件工具类

/**
* @ClassName FileUtils
* @Author ChangLu
* @Date 2021/8/1 18:18
* @Description 文件工具类
*/
public class FileUtil {

/**
* 获取文件名的后缀,如:changlu.jpg => .jpg
* @return 文件后缀名
*/
public static String getFileSuffix(String fileName) {
return fileName.contains(".") ? fileName.substring(fileName.indexOf('.')) : null;
}
}

​GiteeImgBedUtil​​:图床工具类,对外公开了三个API方法用来向Gitee发送请求

import cn.hutool.core.codec.Base64;
import cn.hutool.http.HttpUtil;
import cn.hutool.http.Method;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
* @ClassName UploadGiteeImgBedUtil
* @Author ChangLu
* @Date 2021/12/10 23:41
* @Description Gitee图床工具类
*/
public class GiteeImgBedUtil {


/**
* 码云私人令牌
*/
private static final String ACCESS_TOKEN = ""; //这里不展示我自己的了,需要你自己补充


/**
* 码云个人空间名
*/
private static final String OWNER = "";


/**
* 上传指定仓库
*/
private static final String REPO = "";


/**
* 默认上传时指定存放图片路径
*/
public static final String PATH = "test1/";

//API
/**
* 新建(POST)、获取(GET)、删除(DELETE)文件:()中指的是使用对应的请求方式
* %s =>仓库所属空间地址(企业、组织或个人的地址path) (owner)
* %s => 仓库路径(repo)
* %s => 文件的路径(path)
*/
private static final String API_CREATE_POST = "https://gitee.com/api/v5/repos/%s/%s/contents/%s";


/**
* 生成创建(获取、删除)的指定文件路径
* @param originalFilename 原文件名
* @param path 存储文件路径
* @return
*/
private static String createUploadFileUrl(String originalFilename,String path){
String targetPath = path == null ? GiteeImgBedUtil.PATH : path;
//获取文件后缀
String suffix = FileUtil.getFileSuffix(originalFilename);
//拼接存储的图片名称
String fileName = System.currentTimeMillis()+"_"+ UUID.randomUUID().toString()+suffix;
//填充请求路径
String url = String.format(GiteeImgBedUtil.API_CREATE_POST,
GiteeImgBedUtil.OWNER,
GiteeImgBedUtil.REPO,
targetPath + fileName);
return url;
}

private static String createDelFileUrl(String path){
//填充请求路径
String url = String.format(GiteeImgBedUtil.API_CREATE_POST,
GiteeImgBedUtil.OWNER,
GiteeImgBedUtil.REPO,
path);
return url;
}

private static String createGetUrl(String path){
String targetPath = path == null ? GiteeImgBedUtil.PATH : path;
//填充请求路径
String url = String.format(GiteeImgBedUtil.API_CREATE_POST,
GiteeImgBedUtil.OWNER,
GiteeImgBedUtil.REPO,
targetPath);
return url;
}

/**
* 获取创建文件的请求体map集合:access_token、message、content
* @param multipartFile 文件字节数组
* @return 封装成map的请求体集合
*/
private static Map<String,Object> getUploadBodyMap(byte[] multipartFile){
HashMap<String, Object> bodyMap = new HashMap<>(3);
bodyMap.put("access_token", GiteeImgBedUtil.ACCESS_TOKEN);
bodyMap.put("message", "add file!");
bodyMap.put("content", Base64.encode(multipartFile));
return bodyMap;
}

/**
* 创建普通携带请求体集合内容
* @param map 额外参数
* @param message 请求信息
* @return
*/
private static Map<String,Object> getCommonBodyMap(HashMap map, String message){
HashMap<String, Object> bodyMap = new HashMap<>(2);
bodyMap.put("access_token", GiteeImgBedUtil.ACCESS_TOKEN);
bodyMap.put("message", message);
if (map != null){
bodyMap.putAll(map);
}
return bodyMap;
}

/**
* **********封装好的实际调用方法*******************
*/

//超时
private static int TIMEOUT = 10 * 1000;

/**
* 上传文件
* @param filename 文件名称
* @param path 路径
* @param sha 必备参数from 获取仓库具体路径下的内容
* @return
*/
public static String uploadFile(String path, String originalFilename, byte[] data){
String targetURL = GiteeImgBedUtil.createUploadFileUrl(originalFilename,path);
//请求体封装
Map<String, Object> uploadBodyMap = GiteeImgBedUtil.getUploadBodyMap(data);
return HttpUtil.post(targetURL, uploadBodyMap);
}


/**
* 删除指定path路径下的文件
* @param filename 文件名称
* @param path 路径
* @param sha 必备参数from 获取仓库具体路径下的内容
* @return
*/
public static String deleteFile(String path,String sha){
String delFileUrl = createDelFileUrl(path);
HashMap<String, Object> needMap = new HashMap<>(1);
needMap.put("sha",sha);//添加sha参数
return HttpUtil.createRequest(Method.DELETE, delFileUrl)
.form(getCommonBodyMap(needMap,"del file!")) //构建请求表单
.timeout(TIMEOUT)
.execute().body();
}

/**
* 获取仓库具体路径下的内容,主要是获取 sha
* @param path
* @return
*/
public static String getSha(String path){
String getShaUrl = createDelFileUrl(path);
return HttpUtil.createRequest(Method.GET, getShaUrl)
.form(getCommonBodyMap(null, "get sha!"))
.timeout(TIMEOUT)
.execute().body();
}

}

WebTools.java:常用工具类

/**
* @ClassName WebTools
* @Author ChangLu
* @Date 2021/10/5 23:35
* @Description 常用工具
*/
public class WebTools {

/** 空字符串 */
private static final String NULLSTR = "";

/**
* * 判断一个对象是否为空
*
* @param object Object
* @return true:为空 false:非空
*/
public static boolean isNull(Object object)
{
return object == null;
}


/**
* * 判断一个字符串是否为空串
*
* @param str String
* @return true:为空 false:非空
*/
public static boolean isEmpty(String str)
{
return isNull(str) || NULLSTR.equals(str.trim());
}

/**
* * 判断多个字符串是否为空串
*
* @param str String
* @return true:为空 false:非空
*/
public static boolean areEmpty(String...strs)
{
for (String str : strs) {
if(isEmpty(str)){
return true;
}
}
return false;
}

/**
* * 判断一个字符串是否为非空串
*
* @param str String
* @return true:非空串 false:空串
*/
public static boolean isNotEmpty(String str)
{
return !isEmpty(str);
}


}



Application.yaml

这里要注意配置接口文件的大小,否则一些较大图片上传不了:

# 设置默认运行端口
server:
port: 8081

# max-file-size:servlet每次接收单个文件的最大容量;max-request-size:指的是单次请求接收的文件最大容量
spring:
servlet:
multipart:
max-file-size: 10MB
max-request-size: 100MB

# 自定义
# gitee上传的文件路径(可覆盖图床工具类中的默认存储文件路径)
gitee:
upload:
path: test1/



参考资料

[1]. ​​Vue+Springboot使用Gitee图床上传图片​​

[2]. ​​Gitee—API文档​​


我是长路,感谢你的耐心阅读。如有问题请指出,我会积极采纳!
欢迎关注我的公众号【长路Java】,分享Java学习文章及相关资料
Q群:851968786 我们可以一起探讨学习
注明:转载可,需要附带上文章链接




举报

相关推荐

0 条评论