springboot相应文件流文件给欣赏器+前端下载

[复制链接]
发表于 2026-1-2 02:25:24 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
springboot相应文件流文件给欣赏器+前端下载



1.controller:
  1. @Api(tags = {"【样本提取系统】-api"})
  2. @RestController("YbtqYstbtqController")
  3. @RequiredArgsConstructor
  4. @RequestMapping("/ybtq-ystbtq")
  5. @Slf4j
  6. public class YbtqYstbtqController {
  7. private final YbService ybService;
  8. @ApiOperation(value = "【样本集管理】-样本集导出", notes = "export", produces = "application/octet-stream")
  9.     @PostMapping("/ybgl-set-cloud-download")
  10.     @OpLog("【样本集管理】-样本集导出")
  11.     public void ybGlYbSetDownload(@RequestBody List<String> pkIds, HttpServletResponse response) throws Exception {
  12.         FileDownloadVo fileDownloadVo = tileSetService.ybGlYbSetDownload(pkIds);
  13.         if (null != fileDownloadVo && fileDownloadVo.getFileStream() != null) {
  14.             try (InputStream inputStream = fileDownloadVo.getFileStream();
  15.                  OutputStream outputStream = response.getOutputStream()) {
  16.                 //先将字符串以UTF-8转换成字节数组,再将字节数组以ISO-8859-1转换字符串(标准)
  17.                 String fileName = new String(fileDownloadVo.getFileName().getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1);
  18.                 response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName,"utf-8"));
  19.                 response.setContentType(fileDownloadVo.getContentType());
  20.                 response.setCharacterEncoding("UTF-8");
  21.                 // 响应文件给浏览器
  22.                 IOUtils.copy(inputStream, outputStream);
  23.             } catch (Exception e) {
  24.                 e.printStackTrace();
  25.             }
  26.         } else{
  27.             response.setContentType("application/json;charset=utf-8");
  28.             response.getWriter().write(JSONUtil.toJsonStr(Result.error("所选样本集无有效文件!")));
  29.         }
  30.     }
  31. }
复制代码
文件下载对象FileDownloadVo:
  1. /**
  2. * 文件下载对象
  3. */
  4. @Data
  5. public class FileDownloadVo {
  6.     /**
  7.      * 文件名称
  8.      */
  9.     @ApiModelProperty("文件名称")
  10.     private String fileName;
  11.     /**
  12.      * 文件content-type
  13.      */
  14.     @ApiModelProperty("content-type")
  15.     private String contentType;
  16.     /**
  17.      * 文件流
  18.      */
  19.     @ApiModelProperty("文件流")
  20.     private InputStream fileStream;
  21. }
复制代码
2.serviceImpl:
  1. @Override
  2.     public FileDownloadVo ybGlYbSetDownload(List<String> pkIds) {
  3.         // 文件根目录
  4.         FileDownloadVo ret = new FileDownloadVo();
  5.         // 获取样本集信息
  6.         List<TileSet> ybSetList = tileSetMapper.selectList(new LambdaQueryWrapper<TileSet>().in(TileSet::getPkId, pkIds));
  7.         // 查询到的样本集,影像压缩成一个zip
  8.         if (ybSetList.size() > 0) {
  9.             try {
  10.                 // 获取当前日期并格式化为 yyyyMMdd
  11.                 String currentDate = new SimpleDateFormat("yyyyMMdd").format(new Date());
  12.                 // 创建临时 ZIP 文件
  13.                 File tempZipFile = Files.createTempFile("样本集_" + currentDate + "_", ".zip").toFile();
  14.                 // 创建临时根文件夹用于存放样本文件夹
  15.                 File tempRootDir = Files.createTempDirectory("样本集_" + currentDate + "_").toFile();
  16.                 for (TileSet tileSet : ybSetList) {
  17.                     // 为每个样本集创建一个文件夹
  18.                     File sampleDir = new File(tempRootDir, tileSet.getTileName() + "_" + tileSet.getPkId());
  19.                     sampleDir.mkdirs();
  20.                     // 获取文件复制到临时目录下
  21.                     setTitleSetFile(tileSet, sampleDir);
  22.                 }
  23.                 try (ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(tempZipFile))) {
  24.                     // 遍历 tempRootDir 下的所有子文件夹并添加到 ZIP 文件中
  25.                     File[] subDirs = tempRootDir.listFiles();
  26.                     if (subDirs != null) {
  27.                         for (File subDir : subDirs) {
  28.                             UnZipUtils.zipFiles(subDir, subDir.getName(), zipOut); // 将每个子文件夹递归添加到 ZIP
  29.                         }
  30.                     }
  31.                 } finally {
  32.                     // 删除临时文件夹及其内容
  33.                     UnZipUtils.deleteDirectory(tempRootDir);
  34.                 }
  35.                 try {
  36.                     // 将 ZIP 文件转为 FileInputStream
  37.                     ret.setFileStream(new FileInputStream(tempZipFile));
  38.                 } catch (IOException e) {
  39.                     e.printStackTrace();
  40.                 }
  41.                 // 异步任务:300秒后删除文件
  42.                 UnZipUtils.deleteAsyncFiles(tempZipFile);
  43.             } catch (IOException e) {
  44.                 e.printStackTrace();
  45.             }
  46.         }
  47.         ret.setContentType("application/zip");
  48.         ret.setFileName(String.valueOf(new Date().getTime()));
  49.         return ret;
  50.     }
  51. // 获取文件复制到临时目录下
  52.     private void setTitleSetFile(TileSet tileSet, File sampleDir) {
  53.         String tileSetPath = tileSet.getTilePath();
  54.         boolean isTileSetPath = tileSetPath != null && !Objects.equals(tileSetPath, "");
  55.         if (isTileSetPath) {
  56.             // 获取样本切片file
  57.             List<File> tileSetFileList = UnZipUtils.getFileByImagePaths(tileSetPath);
  58.             for (File file : tileSetFileList) {
  59.                 try {
  60.                     Files.copy(file.toPath(), new File(sampleDir, file.getName()).toPath(), StandardCopyOption.REPLACE_EXISTING);
  61.                 } catch (Exception e) {
  62.                     System.out.print("前时项影像文件路径无效:" + file.getPath());
  63.                 }
  64.             }
  65.         }
  66.     }
  67. /**
  68.      * 辅助方法,用于从ZIP输入流中提取文件
  69.      *
  70.      * @param zis ZIP输入流
  71.      * @param filePath 文件的完整路径
  72.      * @throws IOException 如果发生I/O错误
  73.      */
  74.     public static void extractFile(ZipInputStream zis, String filePath) throws IOException {
  75.         BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePath));
  76.         byte[] bytesIn = new byte[4096];
  77.         int read = 0;
  78.         while ((read = zis.read(bytesIn)) != -1) {
  79.             bos.write(bytesIn, 0, read);
  80.         }
  81.         bos.close();
  82.     }
  83. public static void zipFiles(File fileToZip, String fileName, ZipOutputStream zipOut) throws IOException {
  84.         if (fileToZip.isHidden()) return;
  85.         if (fileToZip.isDirectory()) {
  86.             if (fileName.endsWith("/")) {
  87.                 zipOut.putNextEntry(new ZipEntry(fileName));
  88.                 zipOut.closeEntry();
  89.             } else {
  90.                 zipOut.putNextEntry(new ZipEntry(fileName + "/"));
  91.                 zipOut.closeEntry();
  92.             }
  93.             File[] children = fileToZip.listFiles();
  94.             for (File childFile : children) {
  95.                 zipFiles(childFile, fileName + "/" + childFile.getName(), zipOut);
  96.             }
  97.             return;
  98.         }
  99.         try (FileInputStream fis = new FileInputStream(fileToZip)) {
  100.             ZipEntry zipEntry = new ZipEntry(fileName);
  101.             zipOut.putNextEntry(zipEntry);
  102.             byte[] bytes = new byte[1024];
  103.             int length;
  104.             while ((length = fis.read(bytes)) >= 0) {
  105.                 zipOut.write(bytes, 0, length);
  106.             }
  107.         }
  108.     }
  109. public static void deleteDirectory(File file) {
  110.         File[] contents = file.listFiles();
  111.         if (contents != null) {
  112.             for (File f : contents) {
  113.                 deleteDirectory(f);
  114.             }
  115.         }
  116.         file.delete();
  117.     }
  118. // 异步任务-删除临时文件
  119.     public static void deleteAsyncFiles(File tempZipFile) {
  120.         executorService.submit(() -> {
  121.             try {
  122.                 TimeUnit.SECONDS.sleep(300);
  123.                 Files.deleteIfExists(tempZipFile.toPath());
  124.                 System.out.println("临时压缩包已删除:" + tempZipFile.toPath());
  125.             } catch (Exception e) {
  126.                 System.err.println("临时压缩包删除失败:" + e.getMessage());
  127.             }
  128.         });
  129.     }
  130. public static List<File> getFileByImagePaths(String imagePaths) {
  131.         List<File> ret = new ArrayList<>();
  132.         if (imagePaths != null && !imagePaths.equals("")) {
  133.             List<String> preImagePathList = Arrays.asList(imagePaths.split(","));
  134.             if (preImagePathList.size() > 0) {
  135.                 for (String preImagePath : preImagePathList) {
  136.                     // 根据path获取文件
  137.                     File file = new File(preImagePath);
  138.                     ret.add(file);
  139.                 }
  140.             }
  141.         }
  142.         return ret;
  143.     }
复制代码
3.前端下载:
  1. /**
  2.    * 【样本集管理】-样本集导出
  3.    */
  4.   ybGlYbSetDownload(context = undefined, params = undefined, fileName) {
  5.     const url = `${this.interfaceUrl}/ybtq-ystbtq/ybgl-set-cloud-download`;
  6.     return ajaxHelperInstance.downloadFilePost(url, fileName, {}, params);
  7.   }
  8. async downloadFilePost(url, fileName, urlParam, data = {}, requestMethod = 'POST', vueContext = undefined, spinName = 'spinning', useStreamSaver = false) {
  9.         url = this.prepareUrl(url, {...urlParam, ...data});
  10.         data = convertDateParam(data);
  11.         setVueContextSpinState(vueContext, spinName);
  12.         const requestOptions = this.setupRequestOptions(requestMethod, data);
  13.         try {
  14.             let response;
  15.             if (useStreamSaver) {
  16.                 await this.downloadWithStream(url, fileName, requestOptions);
  17.             } else {
  18.                 response = await axios({url, ...requestOptions});
  19.                 await this.handleResponse(response, fileName);
  20.             }
  21.         } catch (error) {
  22.             this.handleError(error);
  23.         } finally {
  24.             resetVueContextSpinState(vueContext, spinName);
  25.         }
  26.     }
  27. prepareUrl(url, params) {
  28.         const reqData = _.map(params, (val, prop) => ({name: prop, value: val}));
  29.         const queryString = abp.utils.buildQueryString(reqData);
  30.         return url + queryString;
  31.     }
  32. /**
  33. * 转换时间参数,主要处理表单传入参数含有moment对象的问题
  34. * @param obj 传入对象
  35. * @constructor
  36. */
  37. function convertDateParam(obj) {
  38.     _.each(obj, (val, prop) => {
  39.         if (val instanceof moment) {
  40.             obj[prop] = (val as any).format('YYYY-MM-DD HH:mm:ss');
  41.         }
  42.         if (val instanceof Date) {
  43.             obj[prop] = moment(val)
  44.                 .format('YYYY-MM-DD HH:mm:ss');
  45.         }
  46.     });
  47.     return obj;
  48. }
  49. function setVueContextSpinState(vueContext, spinName: string) {
  50.     if (vueContext && vueContext[spinName] != undefined) {
  51.         vueContext[spinName] = true;
  52.     }
  53. }
  54. setupRequestOptions(method, data = null) {
  55.         const headers = {
  56.             'Content-Type': 'application/json',
  57.             'Authorization': 'Bearer ' + window.abp.auth.getToken(),
  58.         };
  59.         return {
  60.             method: method,
  61.             headers: headers,
  62.             responseType: 'blob',
  63.             data: data ? JSON.stringify(data) : undefined,
  64.         };
  65.     }
  66. /**
  67.      * 通过fetch Api进行流式下载
  68.      * @param url 下载地址
  69.      * @param fileName 文件名称
  70.      * @param options 下载选项
  71.      */
  72.     async downloadWithStream(url, fileName, options: any = {}) {
  73.         const {timeout = 1000 * 60 * 180} = options;
  74.         const response = await this.fetchWithTimeout(url, options, timeout);
  75.         if (!response.ok) {
  76.             notification.warning({
  77.                 message: '提示',
  78.                 description: '下载失败',
  79.             });
  80.             throw new Error('下载失败');
  81.         }
  82.         const fileStream = StreamSaver.createWriteStream(fileName);
  83.         const writer = fileStream.getWriter();
  84.         if (response.body.pipeTo) {
  85.             writer.releaseLock();
  86.             return response.body.pipeTo(fileStream);
  87.         }
  88.         const reader = response.body.getReader();
  89.         const pump = () =>
  90.             reader
  91.                 .read()
  92.                 .then(({value, done}) => (done ? writer.close() : writer.write(value).then(pump)));
  93.         return pump();
  94.     }
  95. async handleResponse(response, fileName) {
  96.         const contentType = response.headers['content-type'];
  97.         console.log(contentType);
  98.         if (contentType && contentType.includes('application/json')) {
  99.             const reader = new FileReader();
  100.             reader.onload = () => {
  101.                 try {
  102.                     const jsonData = JSON.parse(reader.result as string);
  103.                     if (jsonData && jsonData.message) {
  104.                         notification.error({
  105.                             message: '提示',
  106.                             description: jsonData.message,
  107.                         });
  108.                     }
  109.                 } catch (error) {
  110.                     console.error('解析json失败:', error);
  111.                 }
  112.             };
  113.             reader.readAsText(response.data);
  114.         } else {
  115.             const blob = new Blob([response.data]);
  116.             const link = document.createElement('a');
  117.             link.href = URL.createObjectURL(blob);
  118.             link.download = fileName;
  119.             link.click();
  120.             URL.revokeObjectURL(link.href);
  121.         }
  122.     }
  123. handleError(error) {
  124.         console.error('下载失败:', error);
  125.     }
  126. function resetVueContextSpinState(vueContext, spinName: string) {
  127.     if (vueContext && vueContext[spinName] == true) {
  128.         vueContext[spinName] = false;
  129.     }
  130. }
复制代码
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表