使用gitee搭建图床,并解决防盗链问题
一、搭建图床(图床—般是指储存图片的服务器)
1、有gitee账号,并搭建一个gitee仓库
点击新建仓库:
填写信息:
新建完就是这个模样了,点击管理:
把仓库弄成共有的这样大家都可以访问,也就是说你的网站可以不用登录你的gitee账号就可以查看你仓库里面的图片:
设置秘钥:
生成新令牌:记住,保存好,如果忘了,只能再生成一个令牌了
提交:
2、我们了解一下怎么上传图片Gitee API 文档,先不深究,熟悉一下,等会看代码
个人Gitee访问路径:我的就是i-dont-recognize-you,可以在设置—>个人资料—>个人空间地址中修改、查看
文件路径:可带文件夹,可以不创建,会自动创建,如:/img/blue.png 如果img这个文件夹不存在就新建这个文件夹并存入blue.png这个图片,如果存在,就直接找到这个文件夹,在里面存入blue.png这个图片。
3、代码编写
导入hutool的工具包
<!-- https://mvnrepository.com/artifact/cn.hutool/hutool-all -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.5.8</version>
</dependency>
配置类(GiteeImgBed)
package bjwl.jk.zg.limsser.util;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 配置码云图床
*/
public class GiteeImgBed {
public static String getNowDate() {
Date now = new Date();
SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd");
return formatter.format(now);
}
//码云私人令牌,需要补充
public static final String ACCESS_TOKEN = "";
//个人空间名,需要补充
public static final String OWNER = "";
//仓库名,需要补充
public static final String REPO_NAME = "";
//文件夹路径
public static final String PATH = "/img/" + getNowDate() + "/";
//message消息
public static final String ADD_MESSAGE = "add img";
public static final String DEl_MESSAGE = "del img";
/**
* 新建文件请求路径
* <p>
* owner* 仓库所属空间地址(企业、组织或个人的地址path)
* repo* 仓库路径
* path* 文件的路径
* content* 文件内容, 要用 base64 编码
* message* 提交信息
* <p>
* %s =>仓库所属空间地址(企业、组织或个人的地址path) (owner)
* %s => 仓库路径(repo)
* %s => 文件的路径(path)
*/
public static String CREATE_REPOS_URL = "https://gitee.com/api/v5/repos/%s/%s/contents/%s";
/**
* 获取路径下所有的内容
* <p>
* owner* 仓库所属空间地址(企业、组织或个人的地址path)
* repo* 仓库路径
* path* 文件的路径
* content* 文件内容, 要用 base64 编码
* message* 提交信息
* <p>
* %s =>仓库所属空间地址(企业、组织或个人的地址path) (owner)
* %s => 仓库路径(repo)
* %s => 文件的路径(path)
*/
public static String GET_IMG_URL = "https://gitee.com/api/v5/repos/%s/%s/contents/%s";
/**
* 删除文件请求路径
* <p>
* owner* 仓库所属空间地址(企业、组织或个人的地址path)
* repo* 仓库路径
* path* 文件的路径
* content* 文件内容, 要用 base64 编码
* message* 提交信息
* <p>
* %s =>仓库所属空间地址(企业、组织或个人的地址path) (owner)
* %s => 仓库路径(repo)
* %s => 文件的路径(path)
*/
public static String DEL_IMG_URL = "https://gitee.com/api/v5/repos/%s/%s/contents/%s";
}
控制器:我这里放了一个更新用户头像的逻辑代码,不需要删掉即可
package com.zhao.controller;
import cn.hutool.core.codec.Base64;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.zhao.common.R;
import com.zhao.pojo.User;
import com.zhao.service.IUserService;
import com.zhao.utils.GiteeImgBed;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
* 上传图床
*/
@RestController
@RequestMapping("/fix/img")
@Transactional(rollbackFor = Exception.class)
public class UploadController {
@Autowired
private IUserService userService;
private final Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* 上传图片
* @param file
* @return
* @throws Exception
*/
@PostMapping("uploadImg")
public R uploadImg(@RequestParam("file") MultipartFile file,@RequestParam("userId") Integer userId) throws Exception {
User user = new User();
user.setUserId(userId);
logger.info("请求成功");
String originaFileName = file.getOriginalFilename();
//上传图片不存在时
if(originaFileName == null){
return R.Failed("上传图片不存在");
}
String suffix = originaFileName.substring(originaFileName.lastIndexOf("."));
//设置图片名字
String fileName = System.currentTimeMillis() + "_" + UUID.randomUUID().toString() + suffix;
String paramImgFile = Base64.encode(file.getBytes());
//设置转存到Gitee仓库参数
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("access_token", GiteeImgBed.ACCESS_TOKEN);
paramMap.put("message", GiteeImgBed.ADD_MESSAGE);
paramMap.put("content", paramImgFile);
//转存文件路径
String targetDir = GiteeImgBed.PATH + fileName;
//设置请求路径
String requestUrl = String.format(GiteeImgBed.CREATE_REPOS_URL, GiteeImgBed.OWNER,
GiteeImgBed.REPO_NAME, targetDir);
logger.info("请求Gitee仓库路径:{}",requestUrl);
String resultJson = HttpUtil.post(requestUrl, paramMap);
JSONObject jsonObject = JSONUtil.parseObj(resultJson);
//表示操作失败
if (jsonObject==null || jsonObject.getObj("commit") == null) {
return R.Failed("操作失败");
}
JSONObject content = JSONUtil.parseObj(jsonObject.getObj("content"));
// 图片路径 这个路径记住后面要讲!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
String img = "https://gitee.com/i-dont-recognize-you/bishe-img/raw/master"+targetDir;
//修改用户头像
user.setImage(img);
userService.updateById(user);
return R.Success(requestUrl);
}
/**
* 删除图片
* @param imgPath
* @return
* @throws Exception
*/
@DeleteMapping("/delImg")
@ResponseBody
public R delImg(@RequestParam(value = "imgPath") String imgPath) throws Exception {
//路径不存在不存在时
if(imgPath == null || "".equals(imgPath.trim())){
return R.Failed("路径不存");
}
String path = imgPath.split("master/")[1];
//上传图片不存在时
if(path == null || "".equals(path.trim())){
return R.Failed("上传图片不存在");
}
//设置请求路径
String requestUrl = String.format(GiteeImgBed.GET_IMG_URL, GiteeImgBed.OWNER,
GiteeImgBed.REPO_NAME, path);
logger.info("请求Gitee仓库路径:{}",requestUrl);
//获取图片所有信息
String resultJson = HttpUtil.get(requestUrl);
JSONObject jsonObject = JSONUtil.parseObj(resultJson);
if (jsonObject == null) {
logger.error("Gitee服务器未响应,message:{}",jsonObject);
return R.Failed("Gitee服务器未响应");
}
//获取sha,用于删除图片
String sha = jsonObject.getStr("sha");
//设置删除请求参数
Map<String,Object> paramMap = new HashMap<>();
paramMap.put("access_token", GiteeImgBed.ACCESS_TOKEN);
paramMap.put("sha", sha);
paramMap.put("message", GiteeImgBed.DEl_MESSAGE);
//设置删除路径
requestUrl = String.format(GiteeImgBed.DEL_IMG_URL, GiteeImgBed.OWNER,
GiteeImgBed.REPO_NAME, path);
logger.info("请求Gitee仓库路径:{}",requestUrl);
//删除文件请求路径
resultJson = HttpRequest.delete(requestUrl).form(paramMap).execute().body();
HttpRequest.put(requestUrl).form(paramMap).execute().body();
jsonObject = JSONUtil.parseObj(resultJson);
//请求之后的操作
if(jsonObject.getObj("commit") == null){
logger.error("Gitee服务器未响应,message:{}",jsonObject);
return R.Failed("Gitee服务器未响应");
}
return R.Success("删除成功");
}
}
JSON工具类:SpringBoot的JSON工具类(java),用于前后端分离_java自带json工具_我认不到你的博客-CSDN博客
4、查看图片:跟着点
此图我放最后,需要留着当壁纸的朋友自取
这个路径https://gitee.com/i-dont-recognize-you/bishe-img/raw/master
跟后面图片的文件夹+文件名就是完整的URL地址
二、解决防盗链问题
1、要解决这个问题,我们先要知道这是什么,并且为什么需要解决这个问题
防盗链是什么?
防盗链不是一根链条,正确的停顿应该是防、盗链——防止其他网站盗用我的链接。
我把图片上传到gitee的服务器,得到了图片的链接,然后拿着这个链接在第三方编辑器中使用,这就是在“盗用”——因为这张图片占用了gitee的服务器资源,却为第三方编辑器工作,gitee得不到好处,还得多花钱。
如何实现防盗链?
要实现防盗链,就需要知道图片的请求是从哪里发出的。可以实现这一功能的有请求头中的
origin
和referer
。origin
只有在XHR请求中才会带上,所以图片资源只能借助referer
。其实gitee也确实是这么做的。通过判断请求的referer,如果请求来源不是本站就返回302,重定向到gitee的logo上,最后在第三方网站引用存在gitee的资源就全变成它的logo了。
可以在开发者工具中看到第三方网站请求gitee图片的流程:
为什么需要解决这个问题?
1、我做毕设或者个人的作品时,我自己的服务器有点小,之前跑了几个服务就有点卡了,而且小编我也没几个钱,不想多买点服务器自己搭,才实习了几个月,白嫖才快乐呀
2、这样我们就可以用别人网站的图片了,岂不美哉,不用维护,gitee稳定,YYDS
解决方案:
1、打个广告,希望大佬们康康,留个小小的赞就行:搭建自己的文件服务器,小编这有两套搭建方案
springboot跨域上传文件(图片)到Linux远程服务器(本地操作也一样)把tomcat作为文件服务器_springboot上传图片到服务器_我认不到你的博客-CSDN博客
使用Docker搭建分布式文件存储系统MinIO_docker minio_我认不到你的博客-CSDN博客
2、使用Nginx做代理
嘿嘿,小编没用过,但所属麻烦了,如果大佬们感兴趣可以自己去搜搜
3、绕过防盗链的防护机制
三种情况下允许引用图片:
- 本网站。
- 无referer信息的情况。(服务器认为是从浏览器直接访问的图片URL,所以这种情况下能正常访问)
- 授权的网址。
上面讲述了3种可以拿到图片的方法,方法1和3能实现吗?肯定不行,所以我们只能从方法2入手了。
实施:
方法1:使用Vue或者html一样的方式
在html文件中加入<meta name="referrer" content="no-referrer">
在vue中的public文件夹下的index.html文件中
<!DOCTYPE html>
<html>
<head>
<!-- 就是这句 屏蔽掉referrer就可以了 -->
<meta name="referrer" content="no-referrer">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="renderer" content="webkit">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= webpackConfig.name %></title>
</head>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
方法2:
可以借助一个chrome小插件:ModHeader (如果你用的是Edge可以点这个链接直接获取),以下图这样设置一下就可以了