fastDFS实现文件上传与下载

前言

我们在做项目的时候经常会遇到文件的上传与下载。你们是怎么做的呢?现在有一个技术可以非常简单的实现这个功能——fastDFS

简介

FastDFS是一个分布式文件系统,使用FastDFS可以非常容易搭建一套高性能的文件服务器集群提供文件上传、下载服务。

原理

它主要包含两个部分, Tracker serverStorage server 客户端请求 Tracker server 进行文件上传、下载,通过 Tracker server 调度最终由 Storage server 完成文件上传和下载。

Tracker server主要是用来进行负载均衡和调度。其实它就是一个调度服务器,通过一些策略找到Storage server来提供文件上传服务。而Storage server的主要作用就是用来进行文件存储。我们上传上来的数据最终也是存储到Storage服务器上的。需要注意的一点就是它本身是没有实现自己的文件系统,而是利用操作系统上的文件系统来管理文件的。所以Storage被称为存储服务器。
在这里插入图片描述
文件上传示意图:
文件上传
文件下载示意图:
在这里插入图片描述

如何使用

  1. 导入依赖
<dependency>
    <groupId>com.github.tobato</groupId>
    <artifactId>fastdfs-client</artifactId>
    <version>1.26.7</version>
    <exclusions>
        <exclusion>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
        </exclusion>
    </exclusions>
</dependency>
  1. 编写配置文件
server.port=8080
# ===================================================================
# 分布式文件系统FDFS配置
# ===================================================================
fdfs.so-timeout = 1501
fdfs.connect-timeout = 601
#缩略图生成参数
fdfs.thumb-image.width= 150
fdfs.thumb-image.height= 150
#TrackerList参数,支持多个
fdfs.tracker-list=10.199.12.106:22122
#访问路径
fdfs.web-server-url=http://10.199.12.106:8888/
  1. 代码
    创建一个工具类
import com.github.tobato.fastdfs.domain.fdfs.StorePath;
import com.github.tobato.fastdfs.domain.proto.storage.DownloadCallback;
import com.github.tobato.fastdfs.service.FastFileStorageClient;
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
@Component
public class FastDFSClientUtil {
    @Autowired
    private FastFileStorageClient storageClient;
    /**
     * 上传
     * @param file 文件类
     * @return fileId 文件Id,包含group和uri的完整路径 EG=>>MO/22/22/**.jpg
     * @throws IOException
     */
    public String uploadFile(MultipartFile file) throws IOException {
        StorePath storePath = storageClient.uploadFile((InputStream) file.getInputStream(), file.getSize(),FilenameUtils.getExtension(file.getOriginalFilename()), null);
        return storePath.getFullPath();
    }
    /**
     * 删除
     * @param filePath 文件的fileId 包含分组,EG=>>group1/M00/22/22/***.**
     */
    public void delFile(String filePath) {
        storageClient.deleteFile(filePath);
    }
    /**
     * 下载
     * @param groupName 分组id  EG=>> group10
     * @param path 文件uri 分组之后的内容[不含开头的/] EG=>>MO/22/22/kkk.jpg
     * @return 文件的字节数组
     */
    public byte[] download(String groupName, String path) throws IOException {
        InputStream ins = storageClient.downloadFile(groupName, path, new DownloadCallback<InputStream>() {
            @Override
            public InputStream recv(InputStream ins) throws IOException {
                // 将此ins返回给上面的ins
                return ins;
            }
        });
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        byte[] buff = new byte[100];
        int rc = 0;
        while ((rc = ins.read(buff, 0, 100)) > 0) {
            byteArrayOutputStream.write(buff, 0, rc);
        }
        return byteArrayOutputStream.toByteArray();
    }
}

实现功能

import com.doria.fastdfs.utils.FastDFSClientUtil;
import com.github.tobato.fastdfs.exception.FdfsServerException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.net.URLEncoder;
@RestController
@RequestMapping("/file")
@Slf4j
public class FileController {
    //  引入客户端工具类
    @Autowired
    private FastDFSClientUtil fastDFSClientUtil;
    // 获取respon稍后向前端页面写入图片
    @Autowired
    private HttpServletResponse response;
    // 读取调度服务所在服务器稍后可以进行拼接,便于下次直接填写,这里由于nginx监听的是80端口,所以没有配置端口。默认访问80
    @Value("${fileServerUrl}")
    private String fileServerUrl;
    @PostMapping("/upload")
    public String uploadFile(MultipartFile file) {
        try {
            // 判断文件是否存在
            if (file == null) {
                throw new RuntimeException("文件不存在");
            }
            // 获取文件的完整名称
            String originalFilename = file.getOriginalFilename();
            if (StringUtils.isEmpty(originalFilename)) {
                throw new RuntimeException("文件不存在");
            }
            // 获取到返回的fileId
            String url = fastDFSClientUtil.uploadFile(file);
            // 拼接返回
            return fileServerUrl + url;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "文件上传失败";
    }
    // 删除文件[这里仅作测试用,实际中不会这样直接在controller删除
    // 是会根据业务从数据库中拿到url进行删除同时删除数据库关联数据,注意被占用无法删除]
    @DeleteMapping("/del")
    public String delFile(@RequestParam String fileId) {
        try {
            fastDFSClientUtil.delFile(fileId);
            return "删除成功";
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "删除失败";
    }
    /**
     * 文件下载,页面直接访问我们这个服务器,在页面眼里,直接从我们这个服务拿到了文件,而我们去fastdfs服务器获取文件
     * 前方的ip端口我们可以固定一个服务器,ip就是当前方法所在服务器的ip,前端找到我们。我们再去给他下载转发
     * 也可以将这个服务部署在fastdfs的本地,然后通过springcloud即时发现服务调用下载服务
     * @param groupName
     * @param path
     */
    @GetMapping("/download")
    public void download(@RequestParam String groupName, @RequestParam String path) {
        try {
            // 拆分获取出文件名称,方便一会写入的时候写出正确的文件名(url中的文件名和服务器中是一致的,至少默认是这样的)
            String[] split = path.split("/");
            String imgName=split[split.length-1];
            // 设置请求头为附件模式
            response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(imgName, "UTF-8"));
            // 调用客户端获取文件字节码
            byte[] imageByte = fastDFSClientUtil.download(groupName, path);
            // 从response获取响应流
            ServletOutputStream outputStream = response.getOutputStream();
            // 向流写入数据
            outputStream.write(imageByte);
            // 关流
            outputStream.close();
        }catch (FdfsServerException e){
            log.error("文件不存在");
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值