spring boot集成FastDFS

本秂侑毒 提交于 2020-10-28 12:09:52

官方文档: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,哎屁事真多哎!

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!