官方文档: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的更多相关文章
-
(转)Spring Boot(十八):使用 Spring Boot 集成 FastDFS
http://www.ityouknow.com/springboot/2018/01/16/spring-boot-fastdfs.html 上篇文章介绍了如何使用 Spring Boot 上传文件 ...
-
Spring Boot(十八):使用 Spring Boot 集成 FastDFS
上篇文章介绍了如何使用 Spring Boot 上传文件,这篇文章我们介绍如何使用 Spring Boot 将文件上传到分布式文件系统 FastDFS 中. 这个项目会在上一个项目的基础上进行构建. ...
-
Spring Boot(十八):使用Spring Boot集成FastDFS
Spring Boot(十八):使用Spring Boot集成FastDFS 环境:Spring Boot最新版本1.5.9.jdk使用1.8.tomcat8.0 功能:使用Spring Boot将文 ...
-
使用Spring Boot集成FastDFS
原文:http://www.cnblogs.com/ityouknow/p/8298358.html#3893468 上篇文章介绍了如何使用Spring Boot上传文件,这篇文章我们介绍如何使用Sp ...
-
Spring Boot集成Jasypt安全框架
Jasypt安全框架提供了Spring的集成,主要是实现 PlaceholderConfigurerSupport类或者其子类. 在Sring 3.1之后,则推荐使用PropertySourcesPl ...
-
Spring boot集成swagger2
一.Swagger2是什么? Swagger 是一款RESTFUL接口的文档在线自动生成+功能测试功能软件. Swagger 是一个规范和完整的框架,用于生成.描述.调用和可视化 RESTful 风格 ...
-
Spring Boot 集成 Swagger,生成接口文档就这么简单!
之前的文章介绍了<推荐一款接口 API 设计神器!>,今天栈长给大家介绍下如何与优秀的 Spring Boot 框架进行集成,简直不能太简单. 你所需具备的基础 告诉你,Spring Bo ...
-
spring boot 集成 zookeeper 搭建微服务架构
PRC原理 RPC 远程过程调用(Remote Procedure Call) 一般用来实现部署在不同机器上的系统之间的方法调用,使得程序能够像访问本地系统资源一样,通过网络传输去访问远程系统资源,R ...
-
Spring Boot 集成Swagger
Spring Boot 集成Swagger - 小单的博客专栏 - CSDN博客https://blog.csdn.net/catoop/article/details/50668896 Spring ...
随机推荐
-
jquery中 $(document).ready()和window.onload的区别
1.执行时间 window.onload必须等到页面内包括图片的所有元素加载完毕后才能执行. $(document).ready()是DOM结构绘制完毕后就执行,不必等到加载完毕. 2.编写个数不同 ...
-
Vue.JS 对比其他框架
Angular 选择 Vue 而不选择 Angular,有下面几个原因,当然不是对每个人都适合: 在 API 与设计两方面上 Vue.js 都比 Angular 简单得多,因此你可以快速地掌握它的全部 ...
-
MySQLdb 1031 Error
Python import MySQLdb 有可能报:site-packages/pkg_resources.py:1031: UserWarning: /home/***/.python-eggs ...
-
web压力测试的轻量级具体做法
一:压力测试中需要掌握的几个基本概念 1:吞吐率(Requests per second) 服务器并发处理能力的量化描述,单位是reqs/s,指的是某个并发用户数下单位时间内处理的请求数.某个并发用户 ...
-
[CSS]position定位
CSS position 属性 通过使用 position 属性,我们可以选择 4 种不同类型的定位,这会影响元素框生成的方式. position 属性值的含义: static 元素框正常生成.块级元 ...
-
poj-3899-The Lucky Numbers 模拟+数学
题目链接: http://poj.org/problem?id=3899 题目意思: 求给定区间内,只含4.7的数的个数以及通过反转后在该区间内的个数和. 解题思路: 模拟+数学. 代码解释的很详细, ...
-
Python数据结构之二叉树
本来打算一个学期分别用C++.Python.Java实现数据结构,看来要提前了 这个是Python版本,我写的数据结构尽量保持灵活性,本文bt1是一般的插入法建立二叉树结构,bt2就是可以任意输入,至 ...
-
Caused by: java.io.FileNotFoundException
1.错误描述 usage: java org.apache.catalina.startup.Catalina [ -config {pathname} ] [ -nonaming ] { -help ...
-
vue ui 启动,浏览器报错Unexpected token <;
可能的解决办法: 选择清空缓存并硬性重新加载页面. 如果不行,全局卸载@vue/cli,重新安装.
-
C# Cookie方法
//写入 protected void Button1_Click(object sender, EventArgs e) { HttpCookie cookie=new HttpCookie(&qu ...