赞
踩
FastDFS可用于分布式系统环境中自行搭建文件服务器,可以达到服务器资源动静分离,请求分流的效果。本篇简单概述下其使用方法。
准备工作:
搭建springboot脚手架并成功运行,可参考历史分享springboot+mybatis
启动FastDFS服务(tracker, storage及nginx)(搭建配置FastDFS服务,后续会在运维章节另行讲述)
<dependency>
<groupId>com.github.tobato</groupId>
<artifactId>fastdfs-client</artifactId>
<version>1.26.7</version>
</dependency>
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})
@SpringBootApplication
public class FdfsApplication {
public static void main(String[] args) {
SpringApplication.run(FdfsApplication.class, args);
}
}
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
*/
@Slf4j
public 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请求代理转发。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。