文件服务器FastDFS

FastDFS可用于分布式系统环境中自行搭建文件服务器,可以达到服务器资源动静分离,请求分流的效果。本篇简单概述下其使用方法。

准备工作:

  1. 搭建springboot脚手架并成功运行,可参考历史分享springboot+mybatis

  2. 启动FastDFS服务(tracker, storage及nginx)(搭建配置FastDFS服务,后续会在运维章节另行讲述)

1. maven添加FastDFS client依赖

<dependency>    <groupId>com.github.tobato</groupId>    <artifactId>fastdfs-client</artifactId>    <version>1.26.7</version></dependency>

2.FastDFS配置

2.1 yml

fdfs:  # 文件下载拉取地址  web-server-url: https://img.xxx.com  so-timeout: 3000  connect-timeout: 1200  thumb-image: # 缩略图    width: 60    height: 60  tracker-list: 192.168.2.9:22122  pool:    jmx-enabled: false #禁止JMX重复注册,影响springboot JMX监控

2.2 FastDFS client config

import com.github.tobato.fastdfs.FdfsClientConfig;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.context.annotation.Import;@Import({FdfsClientConfig.class})@SpringBootApplicationpublic class FdfsApplication {    public static void main(String[] args) {        SpringApplication.run(FdfsApplication.class, args);    }}

3.FastDFS使用

3.1 fdfs util

import com.github.tobato.fastdfs.domain.fdfs.StorePath;import com.github.tobato.fastdfs.exception.FdfsUnsupportStorePathException;import com.github.tobato.fastdfs.exception.FdfsUploadImageException;import com.github.tobato.fastdfs.service.FastFileStorageClient;import lombok.extern.slf4j.Slf4j;import net.coobird.thumbnailator.Thumbnails;import org.apache.commons.io.FilenameUtils;import org.apache.commons.io.IOUtils;import org.apache.commons.lang3.StringUtils;import org.springframework.web.multipart.MultipartFile;import javax.imageio.ImageIO;import java.awt.*;import java.awt.image.BufferedImage;import java.io.*;import java.util.Arrays;import java.util.List;import java.util.UUID;/** * @author Bruce * @date 2021/3/12 */@Slf4jpublic final class FdfsUtil {    private static final List<String> IMAGE_TYPE = 
            Arrays.asList("jpg", "jpeg", "png", "bmp", "tif", "gif", "webp");    private static final List<String> VIDEO_TYPE = Arrays.asList("mp4", "avi");    private static final String IMG_LINUX_LOCATION = "/usr/local/data/img";    /**     * 前端 文件上传     * @param fastFileStorageClient     * @param file     * @param imageServer     * @param needThumbnail     * @param needSize     * @return     * @throws IOException     */    public static UploadFile upload(FastFileStorageClient fastFileStorageClient, 
                                    MultipartFile file, String imageServer, 
                                    Byte needThumbnail, Byte needSize) 
                                    throws IOException {        UploadFile uploadFile = new UploadFile(
                  file.getOriginalFilename(), file.getSize(), file.getContentType());        uploadFile(fastFileStorageClient, file.getInputStream(), 
                   uploadFile, imageServer, needThumbnail, needSize);        return uploadFile;    }    /**     * 后端 文件上传     * @param fastFileStorageClient     * @param file     * @param imageServer     * @param needThumbnail     * @param needSize     * @return     */    public static UploadFile upload(FastFileStorageClient fastFileStorageClient, 
                                    File file, String imageServer, 
                                    Byte needThumbnail, Byte needSize) {        try (InputStream is = new FileInputStream(file)) {            UploadFile uploadFile = new UploadFile(
                       file.getName(), file.length(), null);            uploadFile(fastFileStorageClient, is, uploadFile, 
                       imageServer, needThumbnail, needSize);            return uploadFile;        } catch (Exception e){            throw new CommonException("上传文件失败", e);        }    }    /**     * 上传文件,处理图片     * @param fastFileStorageClient     * @param inputStream     * @param uploadFile     * @param imageServer     * @param needThumbnail     * @param needSize     * @throws IOException     */    private static void uploadFile(FastFileStorageClient fastFileStorageClient, 
                                   InputStream inputStream, UploadFile uploadFile, 
                                   String imageServer, Byte needThumbnail, 
                                   Byte needSize) throws IOException {        log.info("文件上传 originalFileName={}, fileSize={}", 
                 uploadFile.getOriginalFilename(), uploadFile.getFileSize());        // 获取文件后缀        String fileExtName = FilenameUtils.getExtension(
                             uploadFile.getOriginalFilename()).toLowerCase();        // 限制图片文件上传大小        if(isSupportType(fileExtName)){            validateUploadImage(uploadFile, fileExtName);        }        // 缓存文件流        byte[] fileStream = IOUtils.toByteArray(inputStream);        // 上传原图或文件        uploadOrigFile(fastFileStorageClient, uploadFile, 
                      imageServer, fileStream, fileExtName);        // 上传图片缩略图        if(isSupportType(fileExtName)){            uploadImageThumbnails(fastFileStorageClient, uploadFile, 
                  imageServer, fileStream, fileExtName, needThumbnail, needSize);        }        log.info("上传文件地址={}", uploadFile.getFullFilename());    }    /**     * 限制图片文件上传大小     * @param uploadFile     * @param fileExtName     */    private static void validateUploadImage(UploadFile uploadFile, 
                                            String fileExtName){        // gif文件,大小校验,超过4兆,不上传        if("gif".equals(fileExtName)){            if(uploadFile.getFileSize() > 4 * 1024 * 1024){                throw new CommonException("GIF动图大小不能超过4M");            }        } else if(VIDEO_TYPE.contains(fileExtName)){            if(uploadFile.getFileSize() > 40 * 1024 * 1024){                throw new CommonException("视频大小不能超过40M");            }        } else{            // 普通图片文件不能超过10M            if(uploadFile.getFileSize() > 10 * 1024 * 1024){                throw new CommonException("上传单张图片不能超过10M");            }        }    }    /**     * 上传原文件     * @param fastFileStorageClient     * @param uploadFile     * @param imageServer     * @param fileStream     * @param fileExtName     */    private static void uploadOrigFile(FastFileStorageClient fastFileStorageClient, 
                                       UploadFile uploadFile, String imageServer,                                       byte[] fileStream, String fileExtName){        try (ByteArrayInputStream is = new ByteArrayInputStream(fileStream)) {            StorePath originalStorePath = fastFileStorageClient.uploadFile(
                      is, uploadFile.getFileSize(), fileExtName, null);            uploadFile.setFullFilename(imageServer.concat("/").concat(
                       originalStorePath.getFullPath()));        } catch (IOException e) {            log.error("upload Image error", e.getCause());            throw new FdfsUploadImageException("upload Image error", e.getCause());        }    }    /**     * 上传图片缩略图     * @param fastFileStorageClient     * @param uploadFile     * @param imageServer     * @param fileStream     * @param fileExtName     * @param needThumbnail     * @param needSize     * @throws IOException     */    private static void uploadImageThumbnails(
                                        FastFileStorageClient fastFileStorageClient, 
                                        UploadFile uploadFile, String imageServer,
                                        byte[] fileStream, String fileExtName, 
                                        Byte needThumbnail, Byte needSize) 
                                        throws IOException {        // 默认图片缩略图取原图        uploadFile.setThumbnail(uploadFile.getFullFilename());        // 缩略图也需要计算图片尺寸大小        if(Constant.YES.equals(needSize) || Constant.YES.equals(needThumbnail)){            // 获取图片尺寸            BufferedImage bufferedImage = ImageIO.read(new ByteArrayInputStream(
                                          fileStream));            if(bufferedImage != null){                uploadFile.setWidth(bufferedImage.getWidth());                uploadFile.setHeight(bufferedImage.getHeight());                // 压缩处理                if(Constant.YES.equals(needThumbnail)){                    InputStream is = null;                    try {                        ByteArrayOutputStream out = new ByteArrayOutputStream();                        float outputQuality = getOutputQuality(
                                               uploadFile.getFileSize());                        if(!"jpg".equals(fileExtName)){                            // 先转成jpg                            String newPicPath = IMG_LINUX_LOCATION + File.separator 
                                             + UUID.randomUUID().toString() + ".jpg";                            Thumbnails.of(new ByteArrayInputStream(fileStream))
                                                  .scale(1f).toFile(newPicPath);                            // 原比例不变,大小压缩                            Thumbnails.of(newPicPath).scale(1f)
                                  .outputQuality(outputQuality).toOutputStream(out);                            // 删除jpg图片                            new File(newPicPath).delete();                        } else {                            Thumbnails.of(new ByteArrayInputStream(fileStream))
                                        .scale(1f).outputQuality(outputQuality)
                                        .toOutputStream(out);                        }                        is = new ByteArrayInputStream(out.toByteArray());                        // 上传缩略图                        StorePath thumbnailStorePath = fastFileStorageClient
                          .uploadFile(is, is.available(), fileExtName, null);                        uploadFile.setThumbnail(imageServer.concat("/")
                            .concat(thumbnailStorePath.getFullPath()));                        log.info("上传缩略图地址={}", uploadFile.getThumbnail());                    } catch (IOException e) {                        log.error("upload ThumbImage error", e.getCause());                        throw new FdfsUploadImageException(
                                  "upload ThumbImage error",e.getCause());                    } finally {                        IOUtils.closeQuietly(is);                    }                }            }        }    }    /**     * 设置压缩比例     * @param fileSize     * @return     */    private static float getOutputQuality(long fileSize){        if (fileSize < 64 * 1024) {            return 1f;        } else if (fileSize < 256 * 1024) {            return 0.4f;        } else if (fileSize < 2 * 1024 * 1024) {            return 0.2f;        }        return 0.1f;    }    /**     * 判断文件格式是否支持     * @param fileExtName     * @return     */    private static boolean isSupportType(String fileExtName) {        return IMAGE_TYPE.contains(fileExtName) || VIDEO_TYPE.contains(fileExtName);    }    /**     * 删除文件     * @param fileUrl 文件访问地址     * @param fastFileStorageClient     */    public static void deleteFile(FastFileStorageClient fastFileStorageClient, 
                                                               String fileUrl) {        if (StringUtils.isEmpty(fileUrl)) {            return;        }        try {            StorePath storePath = StorePath.parseFromUrl(fileUrl);            fastFileStorageClient.deleteFile(
                                storePath.getGroup(), storePath.getPath());        } catch (FdfsUnsupportStorePathException e) {            log.warn("异常信息:", e);        }    }}

3.2 前端上传文件API

import com.github.tobato.fastdfs.service.FastFileStorageClient;import io.swagger.annotations.Api;import io.swagger.annotations.ApiOperation;import lombok.extern.slf4j.Slf4j;import org.apache.commons.io.FilenameUtils;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Value;import org.springframework.http.MediaType;import org.springframework.http.ResponseEntity;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;import org.springframework.web.multipart.MultipartFile;import java.util.ArrayList;import java.util.List;@Slf4j@RestController@RequestMapping("/fdfs")@Api(tags = "文件上传")public class FdfsController {    @Value("${fdfs.web-server-url}")    private String imageServer;    @Autowired    private FastFileStorageClient fastFileStorageClient;    @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE,
                                    produces = MediaType.APPLICATION_JSON_VALUE)    @ApiOperation(value = "文件上传", httpMethod = "POST")    public String upload(@RequestParam("file") MultipartFile file,     @RequestParam(value = "needSize", defaultValue = "0") Byte needSize,     @RequestParam(value = "needThumbnail", defaultValue = "0") Byte needThumbnail) {        try {            long start = System.currentTimeMillis();            // 获取文件后缀            String fileExtName = FilenameUtils.getExtension(
                                  file.getOriginalFilename()).toUpperCase();            UploadFile uploadFile = FdfsUtil.upload(
                 fastFileStorageClient, file, imageServer, needThumbnail, needSize);            long end = System.currentTimeMillis();            log.info("文件上传耗时:{} 耗秒", (end - start));             return JSON.toJSONString(uploadFile);        } catch (Exception e) {            log.error("上传文件异常:", e);        }        log.warn(String.format("文件 %s 上传失败", file.getOriginalFilename()));        return "上传文件异常";    }}

3.3 服务端上传文件

  @Value("${fdfs.web-server-url}")  private String imageServer;  @Autowired  private FastFileStorageClient fastFileStorageClient;
  File file = new File("xxxx");  UploadFile uploadFile = FdfsUtil.upload(
                    fastFileStorageClient, file, imageServer, null, null);  String fileUrl = uploadFile.getFullFilename();  file.deleteOnExit();  return fileUrl;

4.FastDFS工作过程

  

client 连接请求FastDFS,需借助Nginx做http请求代理转发​。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值