Java多格式文件下载及解压处理
工作中遇到很多多格式文件下載壓縮及解壓處理,現將通用文件下載工具類做一個總結。包含格式(doc/docx、xls/xlsx、lrm/lrmx、txt、zip/rar等)。
一、解壓處理
文件解壓主要處理rar及zip兩種格式壓縮文件。其中rar5.0及以上版本需采用命令行方式解壓,所以rar格式解壓直接使用命令行方式(需注意程序運行環境windows/linux命令行方式不同)。zip格式解壓使用jar包。
解壓所需jar包:
<!-- 解壓zip -->
<dependency><groupId>org.apache.ant</groupId><artifactId>ant</artifactId><version>1.9.4</version>
</dependency>
<!-- io流 -->
<dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.4</version>
</dependency>
通用解壓方法:
/*** 解壓zip文件* @param zipFile* @param outDir* @throws IOException*/
public static void unZip(File zipFile, String outDir) throws IOException {File outFileDir = new File(outDir);if (!outFileDir.exists()) {boolean isMakDir = outFileDir.mkdirs();if (isMakDir) {System.out.println("創建壓縮目錄成功");}}ZipFile zip = new ZipFile(zipFile);for (Enumeration enumeration = zip.getEntries(); enumeration.hasMoreElements(); ) {ZipEntry entry = (ZipEntry) enumeration.nextElement();String zipEntryName = entry.getName();InputStream in = zip.getInputStream(entry);if (entry.isDirectory()) { //處理壓縮文件包含文件夾的情況File fileDir = new File(outDir + zipEntryName);fileDir.mkdir();continue;}File file = new File(outDir, zipEntryName);file.createNewFile();OutputStream out = new FileOutputStream(file);byte[] buff = new byte[1024];int len;while ((len = in.read(buff)) > 0) {out.write(buff, 0, len);}in.close();out.close();}
}/*** 采用命令行方式解壓文件* 解決rar5.0及以上版本壓縮包解壓不了問題* @param rarFile 壓縮文件流* @param destDir 解壓結果路徑* @param cmdPath 命令所在路徑* @return*/
public static boolean unRar(File rarFile, String destDir, String cmdPath) throws Exception {boolean bool = false;if (!rarFile.exists()) {return false;}File destDirPath = new File(destDir);if (!destDirPath.exists()) {destDirPath.mkdirs();}// 開始調用命令行解壓,參數-o+是表示覆蓋的意思// String cmdPath = "C:\\Program Files\\WinRAR\\WinRAR.exe"; // windows中的路徑// String cmdPath = "/usr/local/bin/unrar"; 如果linux做了軟連接 不需要這里配置路徑String cmd = cmdPath + " X -o+ " + rarFile + " " + destDir;Process proc = Runtime.getRuntime().exec(cmd);if (proc.waitFor() != 0) {if (proc.exitValue() == 0) {bool = false;}} else {bool = true;}return bool;
}
需注意的是:
方法僅可以解壓一層壓縮文件,壓縮文件套壓縮文件格式本方法不適用。多層壓縮文件解壓思路:解壓外層壓縮包后依次判斷文件類型,如果檢測到有壓縮包則進行解壓,整個過程可迭代進行。(僅提供思路)
二、文件下載
文件下載在日常工作中很常見,常見的下載格式:doc/docx、xls/xlsx、txt、json、zip等。下載方法都大同小異。
下載使用說明:
- Word:使用的是freemarker生成固定模板進行下載導出。
- Excel:poi導出
- 其他格式:使用io流導出
具體怎么使用freemarker生成模板、poi導出文件本篇不做深入討論(百度一大堆)。
涉及到的jar包(按需添加即可):
<!-- io流 -->
<dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.4</version>
</dependency><!-- poi -->
<dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>3.7</version><scope>compile</scope>
</dependency>
<dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>3.7</version>
</dependency>
<dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml-schemas</artifactId><version>3.7</version>
</dependency>
<dependency><groupId>org.apache.poi</groupId><artifactId>poi-scratchpad</artifactId><version>3.7</version>
</dependency>
<dependency><groupId>org.apache.poi</groupId><artifactId>ooxml-schemas</artifactId><version>1.3</version>
</dependency>
<dependency><groupId>org.apache.xmlbeans</groupId><artifactId>xmlbeans</artifactId><version>2.6.0</version>
</dependency><!-- freemarker -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
通用文件名格式處理工具代碼:
public static String checkEncode(HttpServletRequest request, String value) throws UnsupportedEncodingException {String agent = request.getHeader("USER-AGENT");if (null != agent){if (-1 != agent.indexOf("Chrome") || -1 != agent.indexOf("Firefox") || -1 != agent.indexOf("Safari")) {value = new String(value.getBytes("utf-8"), "ISO8859-1");} else {//IE7+value = java.net.URLEncoder.encode(value, "UTF-8");}}return value;
}
使用io流下載文件
使用io流下載文件適用于很多文件格式,需注意的是字符編碼集。出現文件下載下來中文亂碼的十有八九都是編碼集的問題。
- 示例1:zip文件下載
/*** 根據文件,進行壓縮,批量下載* @throws Exception*/
public static void downloadBatchByFile(List<FileDocDTO> files, HttpServletResponse response, HttpServletRequest request) {ZipOutputStream zos = null;BufferedOutputStream bos = null;try {response.setCharacterEncoding("utf-8");response.setContentType("applicatoin/octet-stream");response.addHeader("Content-Disposition", "attachment; filename=" + checkEncode(request, "cadre.zip"));zos = new ZipOutputStream(response.getOutputStream());bos = new BufferedOutputStream(zos);for (FileDocDTO entry : files) {String fileName = entry.getName(); //每個zip文件名File file = entry.getFile(); //這個zip文件的字節BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));zos.putNextEntry(new ZipEntry(fileName));int len = 0;byte[] buf = new byte[10 * 1024];while ((len = bis.read(buf, 0, buf.length)) != -1) {bos.write(buf, 0, len);}bis.close();bos.flush();}zos.flush();} catch (Exception e) {e.printStackTrace();}finally {try {if (zos != null){zos.close();}if (bos != null){bos.close();}response.getOutputStream().close();}catch (Exception e){e.printStackTrace();}}
}
FileDocDTO類:用于存放需要壓縮的文件名及文件流
@Data
public class FileDocDTO {private String name;private File file;
}
這種壓縮下載方式可壓縮各種格式文件。
- 示例2:lrm/lrmx文件下載(格式為任免表格式)
/*** lrm lrmx下載 -- 瀏覽器下載* @param dataInfoStr 數據信息字符串* @param fileName 文件名稱* @param exportType 文件下載格式 lrm lrmx* @param response* @param request* @return*/
public static boolean singleDataLRMOrLRMXExport(String dataInfoStr, String fileName, String exportType, HttpServletResponse response, HttpServletRequest request){Writer out = null;boolean flag = false;ServletOutputStream servletOutputStream = null;try {String dataName = fileName + "." + exportType;response.setCharacterEncoding("utf-8");response.setContentType("applicatoin/octet-stream");response.addHeader("Content-Disposition", "attachment; filename=" + checkEncode(request, dataName));servletOutputStream = response.getOutputStream();out = new BufferedWriter(new OutputStreamWriter(servletOutputStream));if (exportType.equals("lrmx")){out = new BufferedWriter(new OutputStreamWriter(servletOutputStream, "UTF-8"));}else {out = new BufferedWriter(new OutputStreamWriter(servletOutputStream, "GB2312"));}out.write(dataInfoStr);flag = true;} catch (Exception e){e.printStackTrace();} finally {try {if (out != null){out.close();}if (servletOutputStream != null){servletOutputStream.close();}}catch (Exception e){e.printStackTrace();}}return flag;
}/*** lrm lrmx下載 -- 下載到指定文件夾* @param dataInfoStr 數據信息字符串* @param fileName 文件名稱* @param exportType 文件下載格式 lrm lrmx* @param index 處理下載有多個同名文件 例如: xx(1).lrm* @param filePath 文件指定下載位置* @return*/
public static FileDocDTO singleDataLRMOrLRMXExport(String dataInfoStr, String fileName, String exportType, String index, String filePath){Writer out = null;String sourceFilePath = "";FileDocDTO fileDoc = new FileDocDTO();try {String dataName = fileName + index + "." + exportType;sourceFilePath = filePath + dataName;File file = new File(sourceFilePath);if (!file.exists()) {// 先得到文件的上級目錄,并創建上級目錄,在創建文件file.getParentFile().mkdir();file.createNewFile();}if (exportType.equals("lrmx")){out = new PrintWriter(file, "UTF-8");}else {out = new PrintWriter(file,"GB2312");}out.write(dataInfoStr);fileDoc.setName(dataName);fileDoc.setFile(new File(sourceFilePath));} catch (Exception e){e.printStackTrace();} finally {try {if (out != null){out.close();}}catch (Exception e){e.printStackTrace();}}return fileDoc;
}
提供兩種下載方式,一種直接下載到瀏覽器,另一種下載到指定文件夾。
需注意的是:
這種方式下載也適用于txt、json等格式下載,只需更改文件后綴名和對應的字符編碼。
Word/Excel文件下載
Word/Excel文件下載主要區別在于數據存入形式不一樣。Word需提前固定格式并進行占位符數據替換,Excel可通過代碼自定義數據格式。當然word也可以通過內置代碼進行表格合并等樣式處理。(具體實現操作請百度。內容太多這里不做討論。)
- Word文件下載
/*** word下載 單個下載 -- 瀏覽器下載* @param fileMap* @param wordName 文件名稱* @param templateFilePath 模板位置* @return*/
public static boolean singleDataWordExport(Map fileMap, String wordName, String templateFilePath, HttpServletResponse response, HttpServletRequest request){String fileName = "無效";boolean flag = false;Writer out = null;Template template = null;ServletOutputStream servletOutputStream = null;try {fileName = wordName + ".docx";Configuration configuration = new Configuration(Configuration.VERSION_2_3_28);configuration.setDefaultEncoding("UTF-8");String ftlPath = templateFilePath.substring(0, templateFilePath.lastIndexOf("/"));configuration.setDirectoryForTemplateLoading(new File(ftlPath)); // FTL文件所存在的位置String ftlFile = templateFilePath.substring(templateFilePath.lastIndexOf("/")+1);template = configuration.getTemplate(ftlFile); // 模板文件名response.setCharacterEncoding("utf-8");response.setContentType("applicatoin/octet-stream");response.addHeader("Content-Disposition", "attachment; filename=" + checkEncode(request, fileName));servletOutputStream = response.getOutputStream();out = new BufferedWriter(new OutputStreamWriter(servletOutputStream));template.process(fileMap, out);flag = true;} catch (Exception e) {e.printStackTrace();} finally {try {if (out != null){out.close();}if (servletOutputStream != null){servletOutputStream.close();}}catch (Exception e){e.printStackTrace();}}return flag;
}/*** word下載 單個下載 -- 下載到指定位置* @param fileMap* @param wordName 文件名稱* @param templateFilePath 模板位置* @param index 處理下載有多個同名文件 例如: xx簡歷(1).docx* @param filePath 文件指定下載位置* @return*/
public static FileDocDTO singleDataWordExport(Map fileMap, String wordName, String templateFilePath, String index, String filePath){String fileName = "無效";String sourceFilePath = "";FileOutputStream outPut = null;Writer out = null;Template template = null;FileDocDTO fileDoc = new FileDocDTO();try {fileName = wordName + index + ".docx";Configuration configuration = new Configuration(Configuration.VERSION_2_3_28);configuration.setDefaultEncoding("UTF-8");String ftlPath = templateFilePath.substring(0, templateFilePath.lastIndexOf("/"));configuration.setDirectoryForTemplateLoading(new File(ftlPath)); // FTL文件所存在的位置String ftlFile = templateFilePath.substring(templateFilePath.lastIndexOf("/")+1);template = configuration.getTemplate(ftlFile); // 模板文件名File file = new File(filePath + fileName);if (!file.exists()) {// 先得到文件的上級目錄,并創建上級目錄,在創建文件file.getParentFile().mkdir();file.createNewFile();}outPut = new FileOutputStream(file);out = new BufferedWriter(new OutputStreamWriter(outPut));template.process(fileMap, out);outPut.flush();fileDoc.setName(fileName);fileDoc.setFile(new File(sourceFilePath));} catch (Exception e) {e.printStackTrace();} finally {try {if (outPut != null){outPut.close();}if (out != null){out.close();}}catch (Exception e){e.printStackTrace();}}return fileDoc;
}
- Excel文件下載
excel下載還需導入jar包:
<dependency><groupId>net.sourceforge.jexcelapi</groupId><artifactId>jxl</artifactId><version>2.6.10</version>
</dependency>
示例代碼:
/*** excel格式下載* @param dataList 下載數據* @param cl 數據類* @param fileName 下載文件名稱* @param table_head 表頭數據* @param table_field 表格數據對應字段* @param response* @param request* @return*/
public static boolean excelExport(List dataList, Class cl, String fileName, String[] table_head, String[] table_field, HttpServletResponse response, HttpServletRequest request){boolean flag = false;WritableWorkbook workbook = null;try {// 導出excelresponse.setContentType("application/x-download");response.setCharacterEncoding("utf-8");response.setHeader("Content-Disposition", "attachment;filename=" + checkEncode(request, fileName + ".xls"));workbook = Workbook.createWorkbook(response.getOutputStream());WritableSheet workSheet = workbook.createSheet("first sheet", 0);// 表頭樣式 單元格樣式暫不涉及WritableFont wf_title = new WritableFont(WritableFont.ARIAL, 11, WritableFont.NO_BOLD, false, UnderlineStyle.NO_UNDERLINE, Colour.WHITE);WritableCellFormat wcf_title = new WritableCellFormat(wf_title);Color color = Color.decode("#1f4e78");workbook.setColourRGB(Colour.ORANGE, color.getRed(), color.getGreen(), color.getBlue());wcf_title.setBackground(Colour.ORANGE);wcf_title.setAlignment(Alignment.CENTRE);wcf_title.setVerticalAlignment(VerticalAlignment.CENTRE);wcf_title.setBorder(Border.ALL, BorderLineStyle.THIN, Colour.BLACK);wcf_title.setWrap(true);//構造excel 表頭for (int i = 0; i < table_head.length; i++) {workSheet.addCell(new jxl.write.Label(i, 0, table_head[i], wcf_title));}//填充表格內容Field[] fields = cl.getDeclaredFields();for (int i = 1; i <= dataList.size(); i++) {for (int j = 0; j < table_field.length; j++) {for (int k = 0; k < fields.length; k++) {if (table_field[j].equals(fields[k].getName())) {CellView cellView = new CellView();cellView.setAutosize(true); //設置自動大小fields[k].setAccessible(true);workSheet.setColumnView(j, cellView);if (dataList.get(i - 1) == null) {workSheet.addCell(new jxl.write.Label(j, i, "暫無"));} else {String str = (fields[k].get(dataList.get(i - 1)) + "");if (CommonUtil.isEmpty(str)){str = "";}workSheet.addCell(new jxl.write.Label(j, i, str));}}}}}workbook.write();flag = true;}catch (Exception e){e.printStackTrace();}finally {try {if (workbook != null){workbook.close();}response.getOutputStream().close();}catch (Exception e){e.printStackTrace();}}return flag;
}
好了 暫時總結就這么多。
-------------------------------------------分隔線-------------------------------------------------
2021年2月使用zip解壓遇到一些新問題,問題如下:
1.解壓后文件出現亂碼問題。
2.解壓文件報錯(文件路徑不存在)。
3.解壓文件之后控制臺出現: Cleaning up unclosed ZipFile for archive。
出現問題后經過一系列調試解決結果如下:
1.文件亂碼問題:在解壓時添加字符編碼集
ZipFile zip = new ZipFile(zipFile, "gbk");
2.控制臺出現: Cleaning up unclosed ZipFile for archive;
解決方式:解壓后關閉文件流即可
3.解壓文件報文件路徑不存在:
這是最有意思的一個bug,值得記錄;
場景:
壓縮文件內包含文件夾,文件夾內文件存在數字順序(例:1.學習.docx、2.學習.docx等)。
過程:通過debug一步步調試可看到zip解壓之后出現順序為:文件夾/1.學習.docx、文件夾/2.學習.docx、文件夾/;而之前代碼邏輯順序應為:文件夾/、文件夾/1.學習.docx、文件夾/2.學習.docx;解析出來的順序不一致從而導致問題出現。
問題結論:在做解壓文件時需先做判定文件的上級文件夾是否存在,如不存在需先創建上級文件夾。解壓代碼優化調整如下:
public static void unZip(File zipFile, String outDir) throws IOException {File outFileDir = new File(outDir);if (!outFileDir.exists()) {boolean isMakDir = outFileDir.mkdirs();if (isMakDir) {System.out.println("創建壓縮目錄成功");}}ZipFile zip = new ZipFile(zipFile, "gbk");for (Enumeration enumeration = zip.getEntries(); enumeration.hasMoreElements(); ) {ZipEntry entry = (ZipEntry) enumeration.nextElement();String zipEntryName = entry.getName();InputStream in = zip.getInputStream(entry);if (entry.isDirectory()) { //處理壓縮文件包含文件夾的情況File fileDir = new File(outDir + zipEntryName);fileDir.mkdir();continue;}File file = new File(outDir, zipEntryName);if (!file.getParentFile().exists()){file.getParentFile().mkdir();}file.createNewFile();OutputStream out = new FileOutputStream(file);byte[] buff = new byte[1024];int len;while ((len = in.read(buff)) > 0) {out.write(buff, 0, len);}in.close();out.close();}zip.close();zipFile.delete();}
-------------------------------------------分隔線-------------------------------------------------
補充:Excel單元格合并操作
Excel單元格合并
WritableSheet mergeCells(a,b,c,d)單元格合并函數
參數含義
- a 單元格的列號
- b 單元格的行號
- c 從單元格[a,b]起,向左合并到c列
- b 從單元格[a,b]起,向下合并到d行
注:單元格的列號和行號都是從0開始計算
總結
以上是生活随笔為你收集整理的Java多格式文件下载及解压处理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 单项循环链表
- 下一篇: Python 实例 - Day2 - A