阅读 170

程序员必备小知识系列--SpringBoot文件上传下载以及优化过程 -- 个人笔记

Java IO/NIO/AIO的知识体系图

在这里插入图片描述

博主最开始是用IO实现文件上传下载功能,但发现效率慢,于是使用了NIO

新的输入/输出 (NIO) 库是在 JDK 1.4 中引入的,弥补了原来的 I/O 的不足,提供了高速的、面向块的 I/O。 复制代码

实体类

@Entity public class DpOrder { @Id @GeneratedValue private Integer id; //订单号 private String orderNo; //文件存储的路径(把所有文件保存到数据库里,文件路径之间以逗号相隔) //数据库字段类型为text @Column(columnDefinition = "text") private String filePath; //、、、get、set方法 } 复制代码

file实体类

public class FileBean implements Serializable {     private String filePath;// 文件保存路径     private String fileName;// 文件保存名称     public FileBean() {     }     public String getFilePath() {         return filePath;     }     public void setFilePath(String filePath) {         this.filePath = filePath;     }     public String getFileName() {         return fileName;     }     public void setFileName(String fileName) {         this.fileName = fileName;     } } 复制代码

控制层

@RestController @RequestMapping("api/orderdatamanagement/dpordermanagement") public class DpOrderController extends ExceptionResponse { @Autowired private DpOrderService dpOrderService; //文件上传功能,支持多文件上传(这里传了orderNo参数,是为了在对象中,把文件上传的路径保存到数据库) //根据自己的业务需求,来决定参数,但MultipartFile是一定要的 @RequestMapping(value = "/uploadFile", method = RequestMethod.POST, produces = "multipart/form-data;charset=utf8") @ResponseBody public void uploadFile(@RequestParam("orderNo") String orderNo, @RequestParam("files") MultipartFile[] files) throws IOException { dpOrderService.uploadFile(orderNo, files); } //校验有没有文件 @RequestMapping(value = "/valid-dp-order-download", method = RequestMethod.GET, produces = "multipart/form-data;charset=utf8") public void validDpOrderDownload(@RequestParam("orderNo") String orderNo) { dpOrderService.validDpOrderDownload(orderNo); } //下载文件,把所有文件打包成压缩包下载 @RequestMapping(value = "/download-files-list", method = RequestMethod.GET) public ResponseEntity<byte[]> downFilesList(@RequestParam("orderNo") String orderNo, HttpServletResponse response) { return dpOrderService.downFilesList(orderNo, response); } //查看所有的文件(查看上传后的文件名称) @RequestMapping(value = "/dp-order-file-paths", method = RequestMethod.GET, produces = "application/json;charset=UTF-8") public List<String> getDpOrderByFilePaths(@RequestParam String orderNo) { return dpOrderService.getDpOrderByFilePaths(orderNo); } } 复制代码

业务逻辑层

//业务逻辑层 @Service @Transactional public class DpOrderServiceImpl implements DpOrderService { private static final Logger logger = LoggerFactory.getLogger(DpOrderServiceImpl.class); @Autowired private DpOrderRepository dpOrderRepository; //上传文件 @Override public void uploadFile(String orderNo, MultipartFile[] files) throws IOException { ///////////下面步骤是校验orderNo是否为空,并获取orderNo if (StringUtils.isEmpty(orderNo)) { throw new DpOrderException("订单号为空."); } DpOrder dpOrder = dpOrderRepository.findByOrderNo(no[0]); if (dpOrder == null) { logger.error("订单号不存在."); throw new DpOrderException("订单号不存在."); } String[] no = orderNo.split(","); ////////////////////////////// InputStream in;//定义一个输出流 OutputStream out;//定义一个输入流 //orderFileProperties.getFilePath()是文件存储路径 //I:\\Develop\\postgres\\orderfile为自己本地路径 //也可以改成服务器路径,不过,要写给个读写的权限 String path = orderFileProperties.getFilePath(); File fileDir = new File(path); if (!fileDir.exists()) { fileDir.setWritable(true); boolean mkdir = fileDir.mkdirs(); if (!mkdir) { logger.error("创建文件夹失败."); throw new DpOrderException("创建文件夹失败."); } } //遍历上传的文件 for (MultipartFile file : files) { //获得完整文件名,包括拓展名 String fileName = file.getOriginalFilename(); //获得文件名 String originalName = fileName.substring(0, fileName.lastIndexOf(".")); //获得拓展名 String fileExtensionName = fileName.substring(fileName.lastIndexOf(".") + 1); SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss"); //拼接组成新的文件名 String combinationFileName = originalName + "_" + sdf.format(new Date()) + "." + fileExtensionName; //路径+完整的文件名 File serverFile = new File(fileDir.getAbsolutePath() + "/" + combinationFileName); //输出流,读出数据 in = file.getInputStream(); //输入流,写入数据 out = new FileOutputStream(serverFile); byte[] b = new byte[1024]; int len; //把读出的数据赋予len while ((len = in.read(b)) > 0) { //输出数据 out.write(b, 0, len); } //关闭流 if (out != null) { out.close(); } if (in != null) { in.close(); } //不管FilePath以前有没有值,都要加上,避免存值的时候覆盖以前的值  dpOrder.setFilePath(dpOrder.getFilePath() + combinationFileName + ","); } } //查看上传的文件 @Override public List<String> getDpOrderByFilePaths(String orderNo) { ///////////下面步骤是校验orderNo是否为空,并获取orderNo DpOrder dpOrder = dpOrderRepository.findByOrderNo(orderNo); String str = dpOrder.getFilePath(); if (StringUtils.isEmpty(str)) { return null; } String[] ids = str.split(",");//对存储值以逗号进行分割,以获得每个文件的名称 ////////////////////////////// //定义集合,存储每个文件名称 ArrayList<String> filePathList = new ArrayList<>(); for (int i = 0; i < ids.length; i++) { filePathList.add(ids[i]); } return filePathList; } //校验文件是否为空 @Override public void validDpOrderDownload(String orderNo) { //从传过来的参数查出该对象,判断FilePath字段是否有值,有值表示有文件 DpOrder dpOrder = dpOrderRepository.findByOrderNo(orderNo); String str = dpOrder.getFilePath(); if (StringUtils.isEmpty(str)) { throw new DpOrderException("没有文件可下载.");//抛出异常 } } //下载文件,把所有文件以压缩包的形式进行下载 @Override public ResponseEntity<byte[]> downFilesList(String orderNo, HttpServletResponse response) { ///////////下面步骤是校验orderNo是否为空,并获取orderNo DpOrder dpOrder = dpOrderRepository.findByOrderNo(orderNo); String str = dpOrder.getFilePath(); String[] ids = str.split(","); ////////////////////////////// ArrayList<FileBean> fileList = new ArrayList<>(); //遍历循环得到文件名和文件路径 for (int i = 0; i < ids.length; i++) { FileBean file = new FileBean(); file.setFileName(ids[i]); file.setFilePath(orderFileProperties.getFilePath()); fileList.add(file); } //压缩包名称 String zipName = "download.zip"; //设置压缩包的类型 response.setContentType("application/x-zip-compressed"); response.setHeader("Content-Disposition", "attachment; filename=" + zipName); //设置压缩流:直接写入response,实现边压缩边下载 ZipOutputStream zipos = null; try { zipos = new ZipOutputStream(new BufferedOutputStream(response.getOutputStream())); //设置压缩方法 zipos.setMethod(ZipOutputStream.DEFLATED); } catch (Exception e) { e.printStackTrace(); } DataOutputStream os = null; //循环将文件写入压缩流 for (int i = 0; i < fileList.size(); i++) { String filePath = fileList.get(i).getFilePath(); String fileName = fileList.get(i).getFileName(); File file = new File(filePath + "/" + fileName); try { //添加ZipEntry,并ZipEntry中写入文件流 zipos.putNextEntry(new ZipEntry(fileName)); os = new DataOutputStream(zipos); InputStream is = new FileInputStream(file); byte[] b = new byte[100]; int length; while ((length = is.read(b)) != -1) { os.write(b, 0, length); } is.close(); zipos.closeEntry(); } catch (Exception e) { e.printStackTrace(); } } try { //关闭流 os.flush(); os.close(); zipos.close(); } catch (IOException e) { e.printStackTrace(); } return null; } } 复制代码

application.properties

#单个文件最大 spring.servlet.multipart.max-file-size=20MB #设置总上传数据总大小 spring.servlet.multipart.max-request-size=400MB 复制代码

效果如下

在这里插入图片描述

查看文件

在这里插入图片描述

选择上传文件

在这里插入图片描述

在这里插入图片描述

点击上传

在这里插入图片描述

再次查看文件

在这里插入图片描述

点击下载

在这里插入图片描述

在这里插入图片描述

DpOrder对象中,filePath属性存储文件

在这里插入图片描述

file_path里面的文件名以逗号相隔

在这里插入图片描述

文件上传下载功能已经完成了,但发现效率慢,尤其在服务器上,下载文件要几秒钟,大文件甚至更久

于是,就用效率更快的NIO

优化后的代码

@Override public ResponseEntity<byte[]> downFilesList(String orderNo) { // 循环开始时的当前时间 long starttime = System.currentTimeMillis(); DpOrder dpOrder = dpOrderRepository.findByOrderNo(orderNo); String str = dpOrder.getFilePath(); FileUtil.downZipFile(str, orderFileProperties);//orderFileProperties为路径I:\\Develop\\postgres\\orderfile,这个改成自己实际上的路径 // 循环结束的时间 long endtime = System.currentTimeMillis(); logger.info("下载文件花费的时间为:" + (endtime - starttime) + "毫秒"); return null; } 复制代码

public class FileUtil { public static void downZipFile(String str, OrderFileProperties orderFileProperties) {         String[] ids = str.split(","); //获得系统桌面路径         //FileSystemView fsv = FileSystemView.getFileSystemView();         //File home = fsv.getHomeDirectory();         //String savePath = home.getPath();         String savePath = "D:\\download_files"; // 创建不同的文件夹目录 File downloadFile = new File(savePath); // 判断文件夹是否存在 if (!downloadFile.exists()) { // 如果文件夹不存在,则创建新的的文件夹 downloadFile.mkdirs(); downloadFile.setWritable(true); } //压缩包:路径+随机数+_download         String zipName = savePath + "\\" + FileUtil.random() + "_download.zip";         File zipFile = new File(zipName);         try (ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(zipFile));              WritableByteChannel writableByteChannel = Channels.newChannel(zipOut)) {             zipOut.setMethod(ZipOutputStream.DEFLATED); //遍历循环文件             for (int i = 0; i < ids.length; i++) {                 File file = new File(orderFileProperties.getFilePath() + "/" + ids[i]);                 try (FileChannel fileChannel = new FileInputStream(file).getChannel()) {                     zipOut.putNextEntry(new ZipEntry(ids[i]));                     fileChannel.transferTo(0, file.length(), writableByteChannel);                 }             }         } catch (Exception e) {             e.printStackTrace();         }     } } 复制代码

经测试,下载速度确实快多了,10M的大小的文件需要500左右的ms


作者:exodus3
链接:https://juejin.cn/post/7021526742800007204


文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐