本文最后更新于 80 天前,其中的信息可能已经有所发展或是发生改变。
前言:
众所周知,Github仓库 + jsDelivr 可以做为全球加速的免费图床,上传图片可以使用Picgo,但是视频的话就不太方便了,如果能整合到自己的项目中,通过接口去上传图片/视频,那就会方便很多。
创建 public 仓库
首先需要在 Github 上创建一个开放的仓库
生成获取Access Token
接着需要在个人设置页面中生成 Access Token,生成的时候要记得复制下来保存,不然就看不到了。
SpringBoot 中 yml 文件添加如下配置:
github:
bucket:
# 配置仓库所属的用户名(如果是自己创建的,就是自己的用户名)
user: "springboot-community"
# 配置仓库名称
repository: "twitter-bucket"
# 配置自己的acccessToken
access-token: "996e748cb47117adbf3**********"
url: "https://cdn.jsdelivr.net/gh/${github.bucket.user}/${github.bucket.repository}/"
api: "https://api.github.com/repos/${github.bucket.user}/${github.bucket.repository}/contents/"
cdn.jsdelivr.net 是 jsDelivr 的官方加速节点,但是国内有屏蔽,加速效果不理想,可以换成国内的一些比较稳定的节点,我目前用的是 jsd.onmicrosoft.cn
封装文件上传工具类
如下代码是封装的一个工具类
主要特点是:
使用 UUID 重命名文件,防止冲突
根据日期打散目录,yyyy/mm/dd
package com.wangyuan.utils;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.io.IOException;
import java.time.LocalDate;
import java.util.*;
@Component
public class GithubUploader {
private static final Logger LOGGER = LoggerFactory.getLogger(GithubUploader.class);
public static final String URI_SEPARATOR = "/";
public static final Set<String> ALLOW_FILE_SUFFIX = new HashSet<>(Arrays.asList("jpg", "png", "jpeg", "gif", "mp4"));
@Value("${github.bucket.url}")
private String url;
@Value("${github.bucket.api}")
private String api;
@Value("${github.bucket.access-token}")
private String accessToken;
@Resource
RestTemplate restTemplate;
/**
* 上传文件到Github
*
* @param multipartFile
* @return 文件的访问地址
* @throws IOException
*/
public String upload(MultipartFile multipartFile) throws IOException {
String suffix = this.getSuffix(multipartFile.getOriginalFilename()).toLowerCase();
if (!ALLOW_FILE_SUFFIX.contains(suffix)) {
throw new IllegalArgumentException("不支持的文件后缀:" + suffix);
}
// 重命名文件
String fileName = UUID.randomUUID().toString().replace("-", "") + "." + suffix;
// 目录按照日期打散
String[] folders = this.getDateFolder();
// 最终的文件路径
String filePath = new StringBuilder(String.join(URI_SEPARATOR, folders)).append(fileName).toString();
LOGGER.info("上传文件到Github:{}", filePath);
JsonObject payload = new JsonObject();
payload.add("message", new JsonPrimitive("file upload"));
payload.add("content", new JsonPrimitive(Base64.getEncoder().encodeToString(multipartFile.getBytes())));
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
httpHeaders.set(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE);
httpHeaders.set(HttpHeaders.AUTHORIZATION, "token " + this.accessToken);
ResponseEntity<String> responseEntity = this.restTemplate.exchange(this.api + filePath, HttpMethod.PUT,
new HttpEntity<String>(payload.toString(), httpHeaders), String.class);
if (responseEntity.getStatusCode().isError()) {
// TODO 上传失败
}
JsonObject response = JsonParser.parseString(responseEntity.getBody()).getAsJsonObject();
LOGGER.info("上传完毕: {}", response.toString());
// TODO 序列化到磁盘备份
return this.url + filePath;
}
/**
* 获取文件的后缀
*
* @param fileName
* @return
*/
protected String getSuffix(String fileName) {
int index = fileName.lastIndexOf(".");
if (index != -1) {
String suffix = fileName.substring(index + 1);
if (!suffix.isEmpty()) {
return suffix;
}
}
throw new IllegalArgumentException("非法的文件名称:" + fileName);
}
/**
* 按照年月日获取打散的打散目录
* yyyy/mmd/dd
*
* @return
*/
protected String[] getDateFolder() {
String[] retVal = new String[3];
LocalDate localDate = LocalDate.now();
retVal[0] = localDate.getYear() + "";
int month = localDate.getMonthValue();
retVal[1] = month < 10 ? "0" + month : month + "";
int day = localDate.getDayOfMonth();
retVal[2] = day < 10 ? "0" + day : day + "";
return retVal;
}
}
调用工具类编写上传接口
最后就是在 Controller 层编写上传接口
package com.wangyuan.controller;
import com.wangyuan.common.Result;
import com.wangyuan.utils.GithubUploader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
@CrossOrigin
@RestController
@RequestMapping("/file")
public class FileController {
@Autowired
private GithubUploader githubUploader;
/**
* Github文件上传
*
* @param multipartFile
* @return
* @throws IOException
*/
@PostMapping("/github/upload")
public Object upload(@RequestParam(value = "file") MultipartFile multipartFile) throws IOException {
return Result.success(this.githubUploader.upload(multipartFile));
}
}
上传成功后会返回一串访问的 URL
测试
这里使用 Apipost 进行测试
如图显示,测试成功~
参考资料
在SpringBoot中通过RestTemplate提交文件到Github(白嫖图床)_怎么springboot怎么把图片上传到github里面-CSDN博客