fastDFS实现文件上传与下载

news/2024/7/7 23:46:59

前言

我们在做项目的时候经常会遇到文件的上传与下载。你们是怎么做的呢?现在有一个技术可以非常简单的实现这个功能——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();
        }
    }
}


http://lihuaxi.xjx100.cn/news/1624300.html

相关文章

谷歌、AMD、英特尔加入挑战,英伟达AI解决方案还能继续“遥遥领先”吗?

夕小瑶科技说 原创 编译 | 谢年年 要问世界范围内人工智能解决方案谁最“遥遥领先”&#xff1f; 那肯定是英伟达&#xff01; 然鹅这一情况很有可能会发生变动。 谷歌正在构建自己的人工智能基础设施&#xff01; 除了谷歌&#xff0c;在软件方面&#xff0c;Meta的PyTor…

Steam将强制执行短信验证以遏制肆虐的恶意更新

为应对最近爆发的恶意更新&#xff0c;著名游戏平台Steam出品方Valve近日发布公告称&#xff0c;将为Steam 上发布游戏的开发者实施额外的安全措施&#xff0c;包括基于短信的确认码。 游戏及软件开发商在 Steam 平台上分发其产品需要用到Steamworks&#xff0c;它支持DRM&…

开源六轴机械臂myCobot 280末端执行器实用案例解析

Intrduction 大家好&#xff0c;今天这篇文章的主要内容是讲解以及使用一些myCobot 280 的配件&#xff0c;来了解这些末端执行器都能够完成哪些功能&#xff0c;从而帮助大家能够正确的选择一款适合的配件来进行使用。 本文中主要介绍4款常用的机械臂的末端执行器。 Product m…

安防监控系统EasyCVR视频汇聚平台设备树收藏按钮的细节优化

视频监控TSINGSEE青犀视频平台EasyCVR能在复杂的网络环境中&#xff0c;将分散的各类视频资源进行统一汇聚、整合、集中管理&#xff0c;在视频监控播放上&#xff0c;TSINGSEE青犀视频安防监控汇聚平台可支持1、4、9、16个画面窗口播放&#xff0c;可同时播放多路视频流&#…

网工有35岁焦虑吗

你们好&#xff0c;我是老杨。 应该不少小伙伴发现了吧&#xff0c;俱乐部开始搞直播了&#xff0c;研究了很久&#xff0c;希望能有个新形式&#xff0c;给你输出更多的知识技术和行业咨询。 最近我也一直在学习中&#xff0c;欢迎大家关注俱乐部的视频号&#xff0c;一起共…

只要看完这6个案例,就会刷新你对室内空间颜色搭配的认知

只要看完这6个案例&#xff0c;就会刷新你对室内空间颜色搭配的认知&#xff01; 室内设计和家居装饰灵感因 "芭比娃娃 "电影的上映而席卷了 A&D 行业。随着多年来粉红色的复苏&#xff0c;专家们采用大胆的粉红色调和女性化的装饰也就不足为奇了&#xff0c;而这…

5+甲基化+预后模型搭配实验

今天给同学们分享一篇甲基化预后模型实验的生信文章“Six immune-related promising biomarkers may promote hepatocellular carcinoma prognosis: a bioinformatics analysis and experimental validation”&#xff0c;这篇文章于2023年3月23日发表在Cancer Cell Int期刊上&…

C#网络爬虫实例:使用RestSharp获取Reddit首页的JSON数据并解析

Reddit 是一个非常受欢迎的分享社交新闻聚合网站&#xff0c;用户可以在上面发布和内容。我们的目标是抓取 Reddit 首页的数据 JSON&#xff0c;以便进一步分析和使用。 C#技术概述&#xff1a;C#是一种流行的编程语言&#xff0c;它具有流畅流畅的特点&#xff0c;非常适合开发…