spring boot集成FastDFS

时间:2022-11-08 20:59:53

官方文档:https://github.com/happyfish100/fastdfs-client-java

一、首先,maven工程添加依赖

    <!--fastdfs-->
<dependency>
<groupId>org.csource</groupId>
<artifactId>fastdfs-client-java</artifactId>
<version>1.27-RELEASE</version>
</dependency>

如果报错请参考maven仓库有jar包还是报错怎么办?

二、resources目录下添加fdfs_client.conf文件

前面使用nginx支持http方式访问文件,但所有人都能直接访问这个文件服务器了,所以做一下权限控制。

从来都是我能借用别人的图片,而别人借用我的图片不好意思不行!

FastDFS的权限控制是在服务端开启token验证,客户端根据文件名、当前unix时间戳、秘钥获取token,在地址中带上token参数即可通过http方式访问文件。


#################### FastDFS-Client Start ####################
#默认值为30s
connect_timeout = 10
#默认值为30s
network_timeout = 30
charset = UTF-8
http.tracker_http_port = 80
# token 防盗链功能
http.anti_steal_token = true
http.secret_key = FastDFS1234567890
tracker_server = 192.168.1.118:22122
#tracker_server = 192.168.0.119:22122
## Tracker Server, if more than one, separate with ","
# fastdfs.tracker_servers=10.0.11.201:22122,10.0.11.202:22122,10.0.11.203:22122
#fastdfs.tracker_servers=${tracker_server_addr}:22122
#################### FastDFS-Client End ####################


对应文件服务器上/etc/fdfs下的http.conf

修改http.conf
# vim /etc/fdfs/http.conf 设置为true表示开启token验证
http.anti_steal.check_token=true 设置token失效的时间单位为秒(s)
http.anti_steal.token_ttl=1800 密钥,跟客户端配置文件的fastdfs.http_secret_key保持一致
http.anti_steal.secret_key=FASTDFS1234567890 如果token检查失败,返回的页面
http.anti_steal.token_check_fail=/ljzsg/fastdfs/page/403.html

记得重启服务

三、客户端生成token

访问文件需要带上生成的token以及unix时间戳,所以返回的token是token和时间戳的拼接。

之后,将token拼接在地址后即可访问:file.ljzsg.com/group1/M00/00/00/wKgzgFnkaXqAIfXyAAEoRmXZPp878.jpeg?token=078d370098b03e9020b82c829c205e1f&ts=1508141521

/**
* 获取访问服务器的token,拼接到地址后面
*
* @param filepath 文件路径 group1/M00/00/00/wKgzgFnkTPyAIAUGAAEoRmXZPp876.jpeg
* @param httpSecretKey 密钥
* @return 返回token,如: token=078d370098b03e9020b82c829c205e1f&ts=1508141521
*/
public static String getToken(String filepath, String httpSecretKey){
// unix seconds
int ts = (int) Instant.now().getEpochSecond();
// token
String token = "null";
try {
token = ProtoCommon.getToken(getFilename(filepath), ts, httpSecretKey);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (MyException e) {
e.printStackTrace();
} StringBuilder sb = new StringBuilder();
sb.append("token=").append(token);
sb.append("&ts=").append(ts); return sb.toString();
}

注意事项

如果生成的token验证无法通过,请进行如下两项检查:
  A. 确认调用token生成函数(ProtoCommon.getToken),传递的文件ID中没有包含group name。传递的文件ID格式形如:M00/00/00/wKgzgFnkTPyAIAUGAAEoRmXZPp876.jpeg

  B. 确认服务器时间基本是一致的,注意服务器时间不能相差太多,不要相差到分钟级别。

对比下发现,如果系统文件隐私性较高,可以直接通过fastdfs-client提供的API去访问即可,不用再配置Nginx走http访问。配置Nginx的主要目的是为了快速访问服务器的文件(如图片),如果还要加权限验证,则需要客户端生成token,其实已经没有多大意义。关键是,这里我没找到FastDFS如何对部分资源加token验证,部分开放。有知道的还请留言。

四、封装FastDFS上传工具类

import org.csource.common.NameValuePair;
import org.csource.fastdfs.*;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream; /**
* @Auther: lanhaifeng
* @Date: 2019/4/25 0025 09:11
* @Description:文件上传工具类
*/
public class FastDFSClient {
private static org.slf4j.Logger logger = LoggerFactory.getLogger(FastDFSClient.class); static {
try {
String filePath = new ClassPathResource("fdfs_client.conf").getFile().getAbsolutePath();;
ClientGlobal.init(filePath);
} catch (Exception e) {
logger.error("FastDFS Client Init Fail!",e);
}
} /**
* 文件上传
* @param file 自定义文件上传类
* @return
*/
public static String[] upload(FastDFSFile file) {
logger.info("File Name: " + file.getName() + "File Length:" + file.getContent().length); NameValuePair[] meta_list = new NameValuePair[1];
meta_list[0] = new NameValuePair("author", file.getAuthor()); long startTime = System.currentTimeMillis();
String[] uploadResults = null;
StorageClient storageClient=null;
try {
storageClient = getTrackerClient();
//upload_file()三个参数:@param fileContent ①:文件的内容,字节数组 ②:文件扩展名 ③文件扩展信息 数组
uploadResults = storageClient.upload_file(file.getContent(), file.getExt(), meta_list);
} catch (IOException e) {
logger.error("IO Exception when uploadind the file:" + file.getName(), e);
} catch (Exception e) {
logger.error("Non IO Exception when uploadind the file:" + file.getName(), e);
}
logger.info("upload_file time used:" + (System.currentTimeMillis() - startTime) + " ms");
if (uploadResults == null && storageClient!=null) {
logger.error("upload file fail, error code:" + storageClient.getErrorCode());
}
String groupName = uploadResults[0];
String remoteFileName = uploadResults[1]; logger.info("upload file successfully!!!" + "group_name:" + groupName + ", remoteFileName:" + " " + remoteFileName);
return uploadResults;
} /**
* 查询文件信息
* @param groupName
* @param remoteFileName
* @return
*/
public static FileInfo getFile(String groupName, String remoteFileName) {
try {
StorageClient storageClient = getTrackerClient();
return storageClient.get_file_info(groupName, remoteFileName);
} catch (IOException e) {
logger.error("IO Exception: Get File from Fast DFS failed", e);
} catch (Exception e) {
logger.error("Non IO Exception: Get File from Fast DFS failed", e);
}
return null;
} /**
* 下载文件
* @param groupName 文件路径
* @param remoteFileName 输出流 中包含要输出到磁盘的路径
* @return
*/
public static InputStream downFile(String groupName, String remoteFileName) {
try {
StorageClient storageClient = getTrackerClient();
byte[] fileByte = storageClient.download_file(groupName, remoteFileName);
InputStream ins = new ByteArrayInputStream(fileByte);
return ins;
} catch (IOException e) {
logger.error("IO Exception: Get File from Fast DFS failed", e);
} catch (Exception e) {
logger.error("Non IO Exception: Get File from Fast DFS failed", e);
}
return null;
} /**
* 删除文件
* ==0表示成功
* @param groupName 组名 如:group1
* @param remoteFileName 不带组名的路径名称 如:M00/00/00/wKgRsVjtwpSAXGwkAAAweEAzRjw471.jpg
* @throws Exception
*/
public static int deleteFile(String groupName, String remoteFileName)
throws Exception {
StorageClient storageClient = getTrackerClient();
return storageClient.delete_file(groupName, remoteFileName);
} /**
* 获取storage
* @param groupName 组名
* @return
* @throws IOException
*/
public static StorageServer[] getStoreStorages(String groupName) throws IOException {
TrackerClient trackerClient = new TrackerClient();
TrackerServer trackerServer = trackerClient.getConnection();
return trackerClient.getStoreStorages(trackerServer, groupName);
} /**
*
* @param groupName
* @param remoteFileName
* @return
* @throws IOException
*/
public static ServerInfo[] getFetchStorages(String groupName,String remoteFileName) throws IOException {
TrackerClient trackerClient = new TrackerClient();
TrackerServer trackerServer = trackerClient.getConnection();
return trackerClient.getFetchStorages(trackerServer, groupName, remoteFileName);
} /**
* 获取reacker地址
* @return
* @throws IOException
*/
public static String getTrackerUrl() throws IOException {
return "http://"+getTrackerServer().getInetSocketAddress().getHostString()+":"+ ClientGlobal.getG_tracker_http_port()+"/";
} /**
* 获取tracker连接
* @return
* @throws IOException
*/
private static StorageClient getTrackerClient() throws IOException {
TrackerServer trackerServer = getTrackerServer();
StorageClient storageClient = new StorageClient(trackerServer, null);
return storageClient;
} /**
* 获取tracker服务
* @return
* @throws IOException
*/
private static TrackerServer getTrackerServer() throws IOException {
TrackerClient trackerClient = new TrackerClient();
TrackerServer trackerServer = trackerClient.getConnection();
return trackerServer;
}
}

FastDFSFile
/**
* @Auther: lanhaifeng
* @Date: 2019/4/25 0025 09:15
* @Description:文件信息类
*/
public class FastDFSFile { private String name;//文件名
private byte[] content; //文件的内容,字节数组
private String ext; //文件扩展名,不包含(.)
private String md5; //加密
private String author; public FastDFSFile() {
} public FastDFSFile(String name, byte[] content, String ext) {
this.name = name;
this.content = content;
this.ext = ext;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public byte[] getContent() {
return content;
} public void setContent(byte[] content) {
this.content = content;
} public String getExt() {
return ext;
} public void setExt(String ext) {
this.ext = ext;
} public String getMd5() {
return md5;
} public void setMd5(String md5) {
this.md5 = md5;
} public String getAuthor() {
return author;
} public void setAuthor(String author) {
this.author = author;
}
}
 

五、controller调用

@Controller
public class UploadController {
private static Logger logger = LoggerFactory.getLogger(UploadController.class); @GetMapping("/")
public String index() {
return "upload";
}     
@PostMapping("/upload")
public String singleFileUpload(@RequestParam("file") MultipartFile file,
RedirectAttributes redirectAttributes) {
if (file.isEmpty()) {
redirectAttributes.addFlashAttribute("message", "Please select a file to upload");
return "redirect:uploadStatus";
}
try {
// Get the file and save it somewhere
String path=saveFile(file);
redirectAttributes.addFlashAttribute("message",
"You successfully uploaded '" + file.getOriginalFilename() + "'");
redirectAttributes.addFlashAttribute("path",
"file path url '" + path + "'");
} catch (Exception e) {
logger.error("upload file failed",e);
}
return "redirect:/uploadStatus";
} @GetMapping("/uploadStatus")
public String uploadStatus() {
return "uploadStatus";
} /**
* @param multipartFile
* @return
* @throws IOException 这个方法后面可以自己封装一个util,这里只是测试
*/
public String saveFile(MultipartFile multipartFile) throws IOException {
String[] fileAbsolutePath={};
String fileName=multipartFile.getOriginalFilename();
String ext = fileName.substring(fileName.lastIndexOf(".") + 1);
byte[] file_buff = null;
InputStream inputStream=multipartFile.getInputStream();
if(inputStream!=null){
int len1 = inputStream.available();
file_buff = new byte[len1];
inputStream.read(file_buff);
}
inputStream.close();
FastDFSFile file = new FastDFSFile(fileName, file_buff, ext);
try {
fileAbsolutePath = FastDFSClient.upload(file); //upload to fastdfs
} catch (Exception e) {
logger.error("upload file Exception!",e);
}
if (fileAbsolutePath==null) {
logger.error("upload file failed,please upload again!");
}
String path=FastDFSClient.getTrackerUrl()+fileAbsolutePath[0]+ "/"+fileAbsolutePath[1];
return path;
}
}

参考:https://www.cnblogs.com/Leo_wl/p/7705158.html

  https://blog.csdn.net/qq_26641781/article/details/80913482

当然带了token验证每次访问就得带上token,哎屁事真多哎!

spring boot集成FastDFS的更多相关文章

  1. (转)Spring Boot&lpar;十八&rpar;:使用 Spring Boot 集成 FastDFS

    http://www.ityouknow.com/springboot/2018/01/16/spring-boot-fastdfs.html 上篇文章介绍了如何使用 Spring Boot 上传文件 ...

  2. Spring Boot&lpar;十八&rpar;:使用 Spring Boot 集成 FastDFS

    上篇文章介绍了如何使用 Spring Boot 上传文件,这篇文章我们介绍如何使用 Spring Boot 将文件上传到分布式文件系统 FastDFS 中. 这个项目会在上一个项目的基础上进行构建. ...

  3. Spring Boot&lpar;十八&rpar;:使用Spring Boot集成FastDFS

    Spring Boot(十八):使用Spring Boot集成FastDFS 环境:Spring Boot最新版本1.5.9.jdk使用1.8.tomcat8.0 功能:使用Spring Boot将文 ...

  4. 使用Spring Boot集成FastDFS

    原文:http://www.cnblogs.com/ityouknow/p/8298358.html#3893468 上篇文章介绍了如何使用Spring Boot上传文件,这篇文章我们介绍如何使用Sp ...

  5. Spring Boot集成Jasypt安全框架

    Jasypt安全框架提供了Spring的集成,主要是实现 PlaceholderConfigurerSupport类或者其子类. 在Sring 3.1之后,则推荐使用PropertySourcesPl ...

  6. Spring boot集成swagger2

    一.Swagger2是什么? Swagger 是一款RESTFUL接口的文档在线自动生成+功能测试功能软件. Swagger 是一个规范和完整的框架,用于生成.描述.调用和可视化 RESTful 风格 ...

  7. Spring Boot 集成 Swagger,生成接口文档就这么简单!

    之前的文章介绍了<推荐一款接口 API 设计神器!>,今天栈长给大家介绍下如何与优秀的 Spring Boot 框架进行集成,简直不能太简单. 你所需具备的基础 告诉你,Spring Bo ...

  8. spring boot 集成 zookeeper 搭建微服务架构

    PRC原理 RPC 远程过程调用(Remote Procedure Call) 一般用来实现部署在不同机器上的系统之间的方法调用,使得程序能够像访问本地系统资源一样,通过网络传输去访问远程系统资源,R ...

  9. Spring Boot 集成Swagger

    Spring Boot 集成Swagger - 小单的博客专栏 - CSDN博客https://blog.csdn.net/catoop/article/details/50668896 Spring ...

随机推荐

  1. jquery中 &dollar;&lpar;document&rpar;&period;ready&lpar;&rpar;和window&period;onload的区别

    1.执行时间 window.onload必须等到页面内包括图片的所有元素加载完毕后才能执行. $(document).ready()是DOM结构绘制完毕后就执行,不必等到加载完毕. 2.编写个数不同 ...

  2. Vue&period;JS 对比其他框架

    Angular 选择 Vue 而不选择 Angular,有下面几个原因,当然不是对每个人都适合: 在 API 与设计两方面上 Vue.js 都比 Angular 简单得多,因此你可以快速地掌握它的全部 ...

  3. MySQLdb 1031 Error

    Python import MySQLdb 有可能报:site-packages/pkg_resources.py:1031: UserWarning: /home/***/.python-eggs ...

  4. web压力测试的轻量级具体做法

    一:压力测试中需要掌握的几个基本概念 1:吞吐率(Requests per second) 服务器并发处理能力的量化描述,单位是reqs/s,指的是某个并发用户数下单位时间内处理的请求数.某个并发用户 ...

  5. &lbrack;CSS&rsqb;position定位

    CSS position 属性 通过使用 position 属性,我们可以选择 4 种不同类型的定位,这会影响元素框生成的方式. position 属性值的含义: static 元素框正常生成.块级元 ...

  6. poj-3899-The Lucky Numbers 模拟&plus;数学

    题目链接: http://poj.org/problem?id=3899 题目意思: 求给定区间内,只含4.7的数的个数以及通过反转后在该区间内的个数和. 解题思路: 模拟+数学. 代码解释的很详细, ...

  7. Python数据结构之二叉树

    本来打算一个学期分别用C++.Python.Java实现数据结构,看来要提前了 这个是Python版本,我写的数据结构尽量保持灵活性,本文bt1是一般的插入法建立二叉树结构,bt2就是可以任意输入,至 ...

  8. Caused by&colon; java&period;io&period;FileNotFoundException

    1.错误描述 usage: java org.apache.catalina.startup.Catalina [ -config {pathname} ] [ -nonaming ] { -help ...

  9. vue ui 启动,浏览器报错Unexpected token &lt&semi;

    可能的解决办法: 选择清空缓存并硬性重新加载页面. 如果不行,全局卸载@vue/cli,重新安装.

  10. C&num; Cookie方法

    //写入 protected void Button1_Click(object sender, EventArgs e) { HttpCookie cookie=new HttpCookie(&qu ...