SpringBoot 整合 FastDFS 实现文件上传打包下载
FastDFS
- FastDFS是一个开源的轻量级分布式文件系统,它对文件进行管理,功能包括:文件存储、文件同步、文件访问(文件上传、文件下载)等,解决了大容量存储和负载均衡的问题。特别适合以文件为载体的在线服务,如相册网站、视频网站等等。
1.FastDFS的安装
- 参照 baidu | Google
- docker 安装
2.使用fastdfs-client操作FastDFS
1 2 3 4 5 6 7 8 9 10 11 12
| <!-- fastDFS --> <dependency> <groupId>com.github.tobato</groupId> <artifactId>fastdfs-client</artifactId> <version>1.26.7</version> </dependency>
<dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.6</version> </dependency>
|
3.添加FastDFS的配置
1 2 3 4 5 6 7 8 9 10 11
| # fastDFS 配置 fdfs: soTimeout: 1500 #socket连接超时时长 connectTimeout: 600 #连接tracker服务器超时时长 resHost: 10.6.11.xxx storagePort: 23000 thumbImage: #缩略图生成参数,可选 width: 150 height: 150 trackerList: #TrackerList参数,支持多个,我这里只有一个,如果有多个在下方加- x.x.x.x:port - 10.16.11.xxx:22122
|
4.加载FastDFS的配置信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| package kid1999.upload.config;
import lombok.Data; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component;
/** * @desc: fastDFS配置类 * @auther: kid1999 * @date: 2019/12/19 19:23 **/ @Component @Data public class FastDfsConfig { @Value("${fdfs.resHost}") private String resHost;
@Value("${fdfs.storagePort}") private String storagePort; }
|
5.配置自己需要的FastDFS操作
- 如上传,下载,查看信息,删除 …
- 更多操作 参考源码中的测试案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
| package kid1999.upload.utils;
import com.github.tobato.fastdfs.domain.fdfs.FileInfo; import com.github.tobato.fastdfs.domain.fdfs.StorePath; import com.github.tobato.fastdfs.domain.proto.storage.DownloadByteArray; import com.github.tobato.fastdfs.exception.FdfsUnsupportStorePathException; import com.github.tobato.fastdfs.service.FastFileStorageClient; import kid1999.upload.config.FastDfsConfig; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
/** * @author kid1999 * @desc: * @auther: kid1999 * @date: 2019/12/19 19:25 **/
@Slf4j @Component public class FastDFSClientUtils { @Autowired private FastFileStorageClient storageClient;
@Autowired private FastDfsConfig appConfig; // 项目参数配置
/** * 上传文件 * @param file 文件对象 * @return 文件访问地址 * @throws IOException */ public String uploadFile(MultipartFile file) throws IOException { StorePath storePath = storageClient.uploadFile(file.getInputStream(),file.getSize(), FilenameUtils.getExtension(file.getOriginalFilename()),null); return getResAccessUrl(storePath); }
/** * 查询文件信息 * @param fileUrl * @return FileInfo 远程文件信息 */ public FileInfo getFileInfo(String fileUrl){ if (StringUtils.isEmpty(fileUrl)) { return null; } StorePath storePath = StorePath.parseFromUrl(fileUrl); return storageClient.queryFileInfo(storePath.getGroup(), storePath.getPath()); }
/** * 删除文件 * @param fileUrl 文件访问地址 * @return */ public void deleteFile(String fileUrl) { if (StringUtils.isEmpty(fileUrl)) { return; } try { StorePath storePath = StorePath.parseFromUrl(fileUrl); storageClient.deleteFile(storePath.getGroup(), storePath.getPath()); } catch (FdfsUnsupportStorePathException e) { log.warn(e.getMessage()); } }
/** * 下载文件 * @param fileUrl 文件访问地址 * @return */ public byte[] downloadFile(String fileUrl){ if(StringUtils.isEmpty(fileUrl)){ return null; } try { StorePath storePath = StorePath.parseFromUrl(fileUrl); DownloadByteArray callback = new DownloadByteArray(); byte[] content = storageClient.downloadFile(storePath.getGroup(), storePath.getPath(), callback); return content; }catch (Exception e){ log.warn(e.getMessage()); } return null; }
/** * 更新文件(复写) * @param file * @param oldFileUrl 原文件 * @return 文件路径 * @throws IOException */ public String updateFile(MultipartFile file,String oldFileUrl) throws IOException { deleteFile(oldFileUrl); StorePath storePath = storageClient.uploadFile(file.getInputStream(),file.getSize(), FilenameUtils.getExtension(file.getOriginalFilename()),null); return getResAccessUrl(storePath); }
/** * 封装完整URL地址 * @param storePath */ private String getResAccessUrl(StorePath storePath) { String fileUrl = "http://" + appConfig.getResHost() + ":" + appConfig.getStoragePort() + "/" + storePath.getFullPath(); return fileUrl; } }
|
——-这里已经完成正常的CRUD操作——-
——-下面是一些具体案例——-
6.封装上面完成上传文件和批量打包下载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
| // 文件上传处理 @PostMapping("/upfile") @ResponseBody Result singleFileUpload(@RequestParam("file") MultipartFile file, @RequestParam("workid") int workid, @RequestParam("type") String type, @RequestParam("remarks") String remarks, @RequestParam("name") String sname, @RequestParam("studentClass") String classname, @RequestParam("studentId") String studentno, Model model, HttpServletRequest request) {
Result result = new Result(); if (file.isEmpty()){ return Result.fail(400,"文件不允许为空"); } // 获取来访页面url String referer = request.getHeader("referer"); if(referer != null){ model.addAttribute("referer",referer); }else{ model.addAttribute("referer",request.getHeader("host")); } // 文件信息处理 String fname = file.getOriginalFilename(); String filename = ""; // 姓名-项目名 学号-项目名 学号-姓名-项目名 班级-姓名-项目名 班级-姓名-学号-项目名 // 处理保存的文件名 switch (type){ case "1" : filename = sname + "-" + fname;break; case "2" : filename = studentno + "-" + fname;break; case "3" : filename = studentno + "-" + sname + "-" + fname;break; case "4" : filename = classname + "-" + sname + "-" + fname;break; case "5" : filename = classname + "-" + sname + "-" + studentno + "-" + fname;break; case "6" : filename = sname + "." + fname.substring(fname.lastIndexOf(".") + 1);break; case "7" : filename = sname + "." + fname.substring(fname.lastIndexOf(".") + 1);break; } log.info(sname+ "--" + studentno + "--" + filename ); // 构造student Student newStudent = new Student(); newStudent.setName(sname); newStudent.setClassname(classname); newStudent.setRemarks(remarks); newStudent.setUptime(new Timestamp(System.currentTimeMillis())); newStudent.setWorkid(workid); newStudent.setFilename(filename);
// 先查看是否重复 Student student = studentService.getStudentBySname(workid,sname);
// 文件已提交过了 if(student != null){ try{ newStudent.setId(student.getId()); // 把id带走 String fileUrl = fastDFSClientUtils.updateFile(file,student.getFileurl()); //使用fastDFS写入 newStudent.setFileurl(fileUrl); studentService.updateStudent(newStudent); return Result.success("你已经提交过了,上传成功!"); }catch (Exception e){ return Result.fail(400,"文件上传失败!"); } }
try{ String fileUrl = fastDFSClientUtils.uploadFile(file); newStudent.setFileurl(fileUrl); studentService.addStudent(newStudent); return Result.success("文件上传成功!"); }catch (Exception e){ return Result.fail(400,"文件上传失败!"); } }
|
封装批量打包下载工具
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
| package kid1999.upload.utils;
import kid1999.upload.dto.ZipModel; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletResponse; import java.io.*; import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream;
/** * @desc: * @auther: kid1999 * @date: 2019/12/19 20:21 **/ @Component @Slf4j public class ZipUtil {
@Autowired private FastDFSClientUtils fastDFSClientUtils;
/** * 压缩文件列表中的文件 * * @param files * @param outputStream * @throws IOException */ public void zipFile(List<ZipModel> files, ZipOutputStream outputStream){ try { int size = files.size(); //压缩列表中的文件 for (int i = 0; i < size; i++) { ZipModel zipModel = files.get(i); zipFile(zipModel, outputStream); } } catch (IOException e) { log.error(e.getMessage()); } }
/** * 将文件写入到zip文件中 * * @param zipModel * @param outputstream * @throws IOException */ public void zipFile(ZipModel zipModel, ZipOutputStream outputstream) throws IOException { if (zipModel != null && zipModel.getFilePath() != null && zipModel.getFileName() != null) { log.info(zipModel.getFileName() + ",被下载: " + zipModel.getFilePath()); byte[] content = fastDFSClientUtils.downloadFile(zipModel.getFilePath()); InputStream bInStream = new ByteArrayInputStream(content); ZipEntry entry = new ZipEntry(zipModel.getFileName()); outputstream.putNextEntry(entry); final int MAX_BYTE = 10 * 1024 * 1024; //最大的流为10M long streamTotal = 0; //接受流的容量 int streamNum = 0; //流需要分开的数量 int leaveByte = 0; //文件剩下的字符数 byte[] inOutbyte; //byte数组接受文件的数据
streamTotal = bInStream.available(); //通过available方法取得流的最大字符数 streamNum = (int) Math.floor(streamTotal / MAX_BYTE); //取得流文件需要分开的数量 leaveByte = (int) streamTotal % MAX_BYTE; //分开文件之后,剩余的数量
if (streamNum > 0) { for (int j = 0; j < streamNum; ++j) { inOutbyte = new byte[MAX_BYTE]; //读入流,保存在byte数组 bInStream.read(inOutbyte, 0, MAX_BYTE); outputstream.write(inOutbyte, 0, MAX_BYTE); //写出流 } } //写出剩下的流数据 inOutbyte = new byte[leaveByte]; bInStream.read(inOutbyte, 0, leaveByte); outputstream.write(inOutbyte); outputstream.closeEntry(); bInStream.close(); //关闭 } }
/** * 下载打包的文件 * @param file * @param response */ public void downloadZip(File file, HttpServletResponse response) { try { if (!file.exists()) { file.createNewFile(); } // 以流的形式下载文件。 BufferedInputStream fis = new BufferedInputStream(new FileInputStream(file.getPath())); byte[] buffer = new byte[fis.available()]; fis.read(buffer); fis.close(); // 清空response response.reset();
OutputStream toClient = new BufferedOutputStream(response.getOutputStream()); response.setContentType("application/octet-stream"); response.setHeader("Content-Disposition", "attachment;filename=" + file.getName()); toClient.write(buffer); toClient.flush(); toClient.close(); file.delete(); //将生成的服务器端文件删除 } catch (IOException ex) { ex.printStackTrace(); } }
}
|
调用批量打包下载工具
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| package kid1999.upload.controller;
import kid1999.upload.dto.ZipModel; import kid1999.upload.utils.ZipUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.FileOutputStream; import java.util.ArrayList; import java.util.List; import java.util.zip.ZipOutputStream;
@RestController @Slf4j public class download {
@Autowired private ZipUtil zipUtil;
@PostMapping("download") void download(HttpServletRequest request, HttpServletResponse response) { try { if (request.getParameterValues("filenames") == null) { response.sendRedirect(request.getHeader("REFERER")); } else { List<ZipModel> zipModelList = new ArrayList<>(); String[] filenames = request.getParameterValues("filenames"); for (int i = 0; i < filenames.length; i++) { String[] files = filenames[i].split(" "); zipModelList.add(new ZipModel(files[0], files[1])); } //todo:设置打包后的文件名 String fileName = "File.zip"; //todo:临时文件目录,用于存储打包的下载文件 String globalUploadPath = request.getSession().getServletContext().getRealPath("/"); String outFilePath = globalUploadPath + File.separator + fileName; File file = new File(outFilePath); //文件输出流 压缩流 ZipOutputStream toClient = new ZipOutputStream(new FileOutputStream(file)); //todo:调用通用方法下载fastfds文件,打包成zip文件 zipUtil.zipFile(zipModelList, toClient); toClient.close(); response.setHeader("content-disposition", "attachment;fileName=" + fileName); //todo:将zip文件下载下来 zipUtil.downloadZip(file, response); } } catch (Exception e) { log.error(e.getMessage()); } } }
|