【Java + Vue 实现图片上传后 导出图片及Excel 并压缩为zip压缩包】

发布于:2025-07-23 ⋅ 阅读:(18) ⋅ 点赞:(0)

系统环境:

Java JDK:1.8.0_202
Node.js:v12.2.0
Npm:6.9.0

Java后端实现

Controller

	/**
	 * xxxx-导出
	 * @param response 返回信息体
	 * @param files 上传的图片文件
	 * @param param1 参数1
	 * @param param2 参数2
	 */
	@PostMapping("/exportXX")
	@ApiOperationSupport(order = 13)
	@ApiOperation(value = "导出Excel", notes = "导出Excel")
	@ApiLog("XXX导出")
	public void exportXX(HttpServletResponse response, @RequestParam("files") MultipartFile[] files, @RequestParam("param1") String param1,@RequestParam(value = "param2",defaultValue = "1") String param2) {
		tableExportService.exportXX(response, files, param1, param2);
	}

Service

	/**
	 * XXX-导出
	 */
	void exportXX(HttpServletResponse response, MultipartFile[] files, String param1,String param2);

Impl

	/**
	 * xxxx-导出
	 * @param response 返回信息体
	 * @param files  上传的图片文件
	 * @param param1 参数1
	 * @param param2 参数2
	 */
	@Override
	public void exportXX(HttpServletResponse response,MultipartFile[] files,  String param1, String param2) {
		try {
			// 返回信息体重置
			response.reset();
			// 设置类型
			response.setContentType("application/force-download");
			// 赋值压缩包名称及头部信息
			SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			String fileName = "attachment;filename=cryExcel" + format.format(new Date()) + ".zip";
			response.setHeader("Content-Disposition", fileName);
			// 发送二进制数据到客户端的输出流
			ServletOutputStream servletOutputStream = response.getOutputStream();
			ZipOutputStream zipOut = new ZipOutputStream(servletOutputStream);
			// 图片添加到ZIP
			addPicToZip(files,zipOut);
			// 表头
			List<List<String>> headList = new ArrayList<>();
			// 固定列
			headList.add(Arrays.asList("列1"));
			headList.add(Arrays.asList("列名2"));
			headList.add(Arrays.asList("这是列名3"));
			// 导出数据
			List<List<String>> dataList = new ArrayList<>();
			

			// 自行获取需要导出为excel的数据信息
			List<Map<String, Object>> list = ……;
			// 列表不为空时按照列1进行排序
			if(Func.isNotEmpty(list)){
				list.sort(Comparator.comparing(map -> (String) map.get("列1的键")));
			}
			// 转换数据格式为二维数组 方便存入excel
			for (Map<String, Object> item : list) {
				dataList.add(Arrays.asList(
					String.valueOf(item.getOrDefault("列1的值", "")),
					String.valueOf(item.getOrDefault("列2的值", "")),
					String.valueOf(item.getOrDefault("列3的值", "")) + "%"
				));
			}
			// 导出excel 并合并第一列的相同内容
			int[] mergeColumeIndex = {0};
			int mergeRowIndex = 1;
			String excelName = "导出excel.xlsx";
			File excelfile = new File(excelName);
			if (!excelfile.exists()) {
				excelfile.createNewFile();
			}
			// 将excel写入压缩包
			EasyExcel.write(excelName)
				.head(headList)
				.registerWriteHandler(new ExcelFillCellLineMergeHandler(mergeRowIndex, mergeColumeIndex))
				.sheet("导出excel")
				.doWrite(dataList);
			// 创建 ZipEntry
			ZipEntry entry = new ZipEntry(excelName);
			zipOut.putNextEntry(entry);

			// 读取文件并写入 ZipOutputStream
			try (FileInputStream fis = new FileInputStream(excelfile)) {
				byte[] buffer = new byte[1024];
				int length;
				while ((length = fis.read(buffer)) > 0) {
					zipOut.write(buffer, 0, length);
				}
			}
			// 关闭当前的 ZipEntry
			zipOut.closeEntry();
			// 关流
			zipOut.close();
		} catch (Exception e){
			e.printStackTrace();
		}
	}


/**
 * 将Base64编码的图片文件添加到ZIP输出流中
 * @param files 包含Base64编码的图片的MultipartFile数组
 * @param zipOut 目标ZIP输出流
 */
private void addPicToZip(MultipartFile[] files, ZipOutputStream zipOut) {
    try {
        // 检查文件数组是否为空
        if (files != null) {
            // 遍历所有文件
            for (MultipartFile file : files) {
                // 从MultipartFile中获取输入流并转换为字符串
                String imageData = StreamUtils.copyToString(file.getInputStream(), StandardCharsets.UTF_8);
                // 移除Base64数据前缀(如果存在)
                imageData = imageData.replace("data:image/png;base64,", "");
                // 解码Base64字符串为字节数组
                byte[] imageBytes = Base64.getDecoder().decode(imageData);
                // 创建ZIP条目,使用原始文件名并添加.png扩展名
                ZipEntry zipEntry = new ZipEntry(file.getOriginalFilename() + ".png");
                // 将条目添加到ZIP输出流
                zipOut.putNextEntry(zipEntry);
                // 写入图片字节数据到ZIP条目
                zipOut.write(imageBytes, 0, imageBytes.length);
            }
        }
    } catch (IOException e) {
        // 打印异常堆栈信息
        e.printStackTrace();
    }
}
  • 注:对于此处我个人觉得直接让前端上传file二进制文件更好,后端直接获取file的字节码,然后弄进压缩包,此处可以根据业务需求自行调整~

Vue 前端实现

Api

/**
 * @description 测试导出
 * */
export const exportXX = (data) => {
  return request({
    headers: {
      "Content-Type": "multipart/form-data"// 指定请求体为多部分表单数据(用于文件上传)
    },
    method: 'post',
    responseType: 'blob',// 指定响应类型为二进制大对象(用于接收文件流)
    data,
    url: 'http://localhost:8990/dev-api/tableExport/exportXX',
  })
}

Vue

此处可以自行选择upload组件上传的文件或者echarts图形截图等文件进行上传

    // 上传图片信息
    async uploadImages() {
      try {
        // 创建FormData
        const formData = new FormData();
        if (this.selectedFiles.length !== 0) {
          // 处理每个文件
          for (const file of this.selectedFiles) {
            // 转换为完整的Base64 DataURL(包含前缀)
            const base64DataUrl = await this.convertToBase64(file);
            // 创建一个Blob对象,内容为Base64字符串(作为文本)
            const blob = new Blob([base64DataUrl], { type: "text/plain" });
            // 使用原始文件名创建File对象
            const fileToUpload = new File([blob], file.name, {
              type: "text/plain",
            });
            // 添加到FormData
            formData.append("files", fileToUpload);
          }
        }
        formData.append("param1", "111");
        formData.append("param2", "222");
        // 调用导出接口
        await exportXX(formData).then((res) => {
          console.log(formData);
          console.log(res);
          if(res){
            const elink = document.createElement('a');
            elink.download = '文件名称.zip';
            elink.style.display = 'none';
            const blob = new Blob([res], { type: 'application/x-msdownload' });
            elink.href = URL.createObjectURL(blob);
            document.body.appendChild(elink);
            elink.click();
            document.body.removeChild(elink);
          }else {
            this.$message.error('导出异常请联系管理员');
          }
        });

        console.log("上传成功");
      } catch (error) {
        console.error("上传错误:", error);
      }
    },
    //转换为base64字符
    convertToBase64(file) {
      return new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.onload = () => resolve(reader.result); // 返回完整的DataURL(含前缀)
        reader.onerror = reject;
        reader.readAsDataURL(file);
      });
    },

网站公告

今日签到

点亮在社区的每一天
去签到