【Java + Vue 实现图片上传后 导出图片及Excel 并压缩为zip压缩包】
系统环境:
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*/@Overridepublic 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);// 图片添加到ZIPaddPicToZip(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的键")));}// 转换数据格式为二维数组 方便存入excelfor (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);// 创建 ZipEntryZipEntry entry = new ZipEntry(excelName);zipOut.putNextEntry(entry);// 读取文件并写入 ZipOutputStreamtry (FileInputStream fis = new FileInputStream(excelfile)) {byte[] buffer = new byte[1024];int length;while ((length = fis.read(buffer)) > 0) {zipOut.write(buffer, 0, length);}}// 关闭当前的 ZipEntryzipOut.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 {// 创建FormDataconst 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",});// 添加到FormDataformData.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);});},