1. 程式人生 > 實用技巧 >JAVA專案實戰-實現生成固定格式PDF檔案和打包成zip壓縮包並在瀏覽器中輸出

JAVA專案實戰-實現生成固定格式PDF檔案和打包成zip壓縮包並在瀏覽器中輸出

1.工具

// 生成PDF自定義模板內容

(1)Adobe Acrobat Pro9

2.操作步驟

(1)利用Adobe Acrobat Pro9 生成一張根據業務場景的PDF,設定每個內容的欄位(這款軟體功能比較強大,可以設定條形碼和二維碼的引數)

(3)JAVA實現程式碼

  1 import com.itextpdf.text.pdf.PdfReader;
  2 import lombok.extern.slf4j.Slf4j;
  3 import org.springframework.beans.factory.annotation.Value;
  4 import org.springframework.stereotype.Component;
5 import org.springframework.util.ClassUtils; 6 import org.springframework.util.CollectionUtils; 7 import org.springframework.util.ResourceUtils; 8 9 import javax.servlet.ServletOutputStream; 10 import javax.servlet.http.HttpServletResponse; 11 import java.io.*; 12 import java.util.HashMap;
13 import java.util.List; 14 import java.util.UUID; 15 16 /** 17 * @description: PDF下載 18 * @author: ZhuCJ 19 * @date: 2020-05-20 15:36 20 */ 21 @Slf4j 22 @Component 23 public class PdfDownUtils { 24 25 /** 最終存放pdf位置 */ 26 @Value("${pdf.savePath}") 27 private String savePath;
28 29 /** 讀取模板,生成的複製pdf位置 */ 30 @Value("${pdf.cachePath}") 31 private String cacheTempPath; 32 33 /**讀取模板的位置 */ 34 @Value("${pdf.tempPath}") 35 private String tempPath; 36 37 /** 38 * 讀取的模板名字 39 */ 40 public static String TEMPLATE_NAME = "temp.pdf"; 41 42 43 /** 44 * 下載單張PDF 45 * @param mapPDF 46 * @param fileName 47 * @param filePath 48 * @param response 49 * @throws IOException 50 */ 51 public void pdfCompress(HashMap<String,String> mapPDF,String fileName 52 ,String filePath, HttpServletResponse response) throws IOException{ 53 54 String sysPath = ClassUtils.getDefaultClassLoader().getResource("").getPath(); 55 //專案下模板路徑 56 //String readTempPath = sysPath +"data"+File.separator+"hanzt"+File.separator+"temp"; 57 //讀取模板檔案 58 PdfReader reader = new PdfReader(tempPath + File.separator + TEMPLATE_NAME); 59 //生成pdf 60 InputStream pdfStream = null; 61 try { 62 pdfStream = this.print(reader, mapPDF,fileName,filePath); 63 } catch (IOException e) { 64 e.printStackTrace(); 65 } 66 ServletOutputStream op = null; 67 try { 68 op = response.getOutputStream(); 69 } catch (IOException e) { 70 e.printStackTrace(); 71 } 72 response.setContentType("application/pdf"); 73 response.setHeader("Content-Disposition", "inline; filename=\"" 74 + new String(fileName.getBytes("gb18030"), "ISO8859-1") + ".pdf" + "\""); 75 int length = 0; 76 byte[] bytes = new byte[1024]; 77 while((pdfStream != null) && ((length = pdfStream.read(bytes)) != -1)) { 78 op.write(bytes, 0, length); 79 } 80 op.close(); 81 reader.close(); 82 response.flushBuffer(); 83 } 84 85 86 87 /** 88 * 生成pdf打成ZIP包下載 89 * @param orderZips 模板引數 90 * @param filePath pdf 儲存檔案上級資料夾名 91 * @param response 92 * @throws IOException 93 */ 94 public void pdfCompressZip(List<List<HashMap<String,Object>>> orderZips, 95 String filePath, HttpServletResponse response) throws IOException{ 96 if (CollectionUtils.isEmpty(orderZips)){ 97 return; 98 } 99 100 for (List orderZip:orderZips){ 101 if (CollectionUtils.isEmpty(orderZip)){ 102 continue; 103 } 104 for (Object orderPdf:orderZip){ 105 HashMap<String,Object> mapPDF =(HashMap<String,Object>) orderPdf; 106 //獲取生成pdf的檔名 107 String fileName = null; 108 if (mapPDF.containsKey("fileName")){ 109 fileName = mapPDF.get("fileName").toString(); 110 }else { 111 //預設隨機生成一個,保證唯一性避免覆蓋 112 fileName = UUID.randomUUID().toString(); 113 } 114 try { 115 this.printFilePath(mapPDF, fileName, filePath); 116 } catch (IOException e) { 117 log.error("生成Pdf檔案IO異常:{}",e.getMessage()); 118 } 119 } 120 } 121 //本次操作儲存pdf檔案路徑,用於壓縮成Zip包 122 String pdfFilePath = savePath+File.separator+filePath+File.separator; 123 log.info("待壓縮zip包檔名:{}",pdfFilePath); 124 File file = new File(pdfFilePath); 125 String zipFile = null; 126 File ftp = null; 127 try { 128 zipFile = CompressZipUtil.zipFile(file,"zip"); 129 } catch (Exception e) { 130 e.printStackTrace(); 131 } 132 response.setContentType("APPLICATION/OCTET-STREAM"); 133 response.setHeader("Content-Disposition","attachment; filename=listDown.zip"); 134 OutputStream out = null; 135 InputStream in = null; 136 try { 137 out = response.getOutputStream(); 138 // 139 ftp = ResourceUtils.getFile(zipFile); 140 in = new FileInputStream(ftp); 141 // 迴圈取出流中的資料 142 byte[] b = new byte[100]; 143 int len; 144 while ((len = in.read(b)) !=-1) { 145 out.write(b, 0, len); 146 } 147 } catch (Exception e) { 148 e.printStackTrace(); 149 log.error("檔案讀取異常:{}",e.getMessage()); 150 151 }finally { 152 if (in !=null){ 153 in.close(); 154 } 155 if (out !=null){ 156 out.close(); 157 } 158 log.info("zip下載完成,進行刪除本地zip包"); 159 //刪除儲存的Pdf檔案 160 DeleteFileUtil.deleteFile(file); 161 //刪除儲存的壓縮包 162 if (ftp!=null){ 163 ftp.delete(); 164 } 165 } 166 } 167 168 /** 169 * 170 * @param map 171 * @param fileName 172 * @return 所在檔案地址 173 * @throws IOException 174 */ 175 private String printFilePath(HashMap<String,Object> map 176 ,String fileName,String filePath) throws IOException { 177 String sysPath = ClassUtils.getDefaultClassLoader().getResource("").getPath(); 178 //專案下模板路徑 179 // String readTempPath = sysPath +"data"+File.separator+"hanzt"+File.separator+"temp"; 180 //判斷是否存在檔案目錄,不存在建立 181 createFile(savePath,cacheTempPath); 182 //儲存路徑+隨機檔名 183 String save = savePath+ File.separator+filePath+File.separator; 184 PdfFormater pdf = new PdfFormater(tempPath, save,cacheTempPath,TEMPLATE_NAME,map); 185 pdf.doTransform(fileName); 186 return save; 187 } 188 189 190 /** 191 * 列印,以PDF為模板 192 * @param templateName String 模板名字 193 * @param map 模板資料HashMap 194 * @return InputStream 195 * @throws IOException 196 */ 197 private InputStream print(PdfReader reader, HashMap<String,String> map, String fileName, String filePath) throws IOException { 198 InputStream is = null; 199 String sysPath = ClassUtils.getDefaultClassLoader().getResource("").getPath(); 200 //專案下模板路徑 201 //String readTempPath = sysPath +"data"+File.separator+"hanzt"+File.separator+"temp"; 202 //判斷是否存在檔案目錄,不存在建立 203 createFile(savePath,cacheTempPath); 204 //儲存路徑+隨機檔名 205 String save = savePath+ File.separator+filePath+File.separator; 206 PdfFormater pdf = new PdfFormater(tempPath,save,cacheTempPath,TEMPLATE_NAME, map); 207 String PdfFilePath = pdf.doTransform(fileName); 208 is = new FileInputStream(PdfFilePath); 209 return is; 210 } 211 212 213 214 /** 215 * 判斷資料夾是否存在,不存在建立一個 216 * @param filePaths 217 * @return 218 */ 219 public void createFile(String ... filePaths){ 220 for (String filePath:filePaths){ 221 File file = new File(filePath); 222 if (!file.exists()){ 223 file.mkdirs(); 224 } 225 } 226 } 227 228 }
  1 import com.itextpdf.text.BadElementException;
  2 import com.itextpdf.text.DocumentException;
  3 import com.itextpdf.text.Image;
  4 import com.itextpdf.text.Rectangle;
  5 import com.itextpdf.text.pdf.*;
  6 import com.sf.vsolution.hb.sfce.util.string.StringUtils;
  7 import lombok.extern.slf4j.Slf4j;
  8 
  9 import java.io.File;
 10 import java.io.FileOutputStream;
 11 import java.io.IOException;
 12 import java.lang.reflect.Field;
 13 import java.util.Iterator;
 14 import java.util.List;
 15 import java.util.Map;
 16 import java.util.Objects;
 17 
 18 /**
 19  * @description:
 20  * @author: ZhuCJ 
 21  * @date: 2020-05-27 10:50
 22  */
 23 @Slf4j
 24 public class PdfFormater {
 25     /**
 26      * pdf模板路徑
 27      */
 28     private String templatePath;
 29     /**
 30      * 下載完成的pdf路徑
 31      */
 32     private String savePath;
 33     /**
 34      * 快取pdf路徑
 35      */
 36     private String cachePath;
 37 
 38     /**
 39      * 讀取模板物件
 40      */
 41     private String templateName;
 42 
 43     /**
 44      * 需要填充的資料
 45      */
 46     private Map dataMap;
 47 
 48     private String cacheFileName;
 49 
 50     //新的PDF檔名稱
 51     private String resultFileName;
 52     //動態資料
 53     private List dynData;
 54 
 55 
 56     /**
 57        * 構造器,生成PDF引擎例項,並引入相應模板檔案XXX.FO、路徑和報表資料HashMap
 58        *
 59        * @param templateDir
 60        * 模板檔案所在目錄
 61        * @param basePath
 62        * 模板檔案工作副本及結果PDF檔案所在工作目錄
 63        * @param templateFileFo
 64        * 模板檔名,推薦格式為“XXXTemplate.FO”, 其檔案由word模板文件在設計時轉換而成
 65        * @param dataMap
 66        * 對應模板的資料HashMap,由呼叫該列印引擎的里程根據模板格式和約定進行準備
 67        */
 68     public PdfFormater(String templatePath, String savePath, String cachePath,
 69                        String templateName, Map dataMap) {
 70         this.templatePath = templatePath;
 71         this.savePath = savePath;
 72         this.templateName = templateName;
 73         this.cachePath = cachePath;
 74         this.dataMap = dataMap;
 75     }
 76 
 77     /**
 78      * 設定字型
 79      * @param font
 80      * @return
 81      */
 82     private BaseFont getBaseFont(String font) {
 83       // 需要根據不同的模板返回字型
 84         BaseFont bf = null;
 85         try {
 86             bf = BaseFont.createFont( StringUtils.isEmpty(font)?"STSong-Light":font, "UniGB-UCS2-H",BaseFont.NOT_EMBEDDED);
 87         } catch (DocumentException e) {
 88             e.printStackTrace();
 89         } catch (IOException e) {
 90             e.printStackTrace();
 91         }
 92         return bf;
 93     }
 94 
 95     /**
 96      * 避免出現多執行緒重複讀
 97      * @param fileName
 98      * @return
 99      */
100     public String doTransform(String fileName) {
101         long name = System.currentTimeMillis();
102         //快取模板名字
103         cacheFileName =   name + ".pdf";
104         //最後儲存模板名字
105         resultFileName =  fileName + ".pdf";
106         try {
107             PdfReader reader;
108             PdfStamper stamper;
109             //讀取PDF模板物件
110             reader = new PdfReader(templatePath + File.separator + templateName);
111             //生成新的PDF模板物件
112             stamper = new PdfStamper(reader, new FileOutputStream(cachePath + File.separator + cacheFileName));
113             AcroFields form = stamper.getAcroFields();
114             form.addSubstitutionFont(getBaseFont(""));
115             transformRegular(form,stamper);
116             stamper.setFormFlattening(true);
117             stamper.close();
118             reader.close();
119             postProcess();
120         } catch (Exception e) {
121             e.printStackTrace();
122         }
123         return savePath + File.separator + resultFileName;
124     }
125 
126          /**
127        * 填充規整的表單域
128        * @param form
129        */
130     private void transformRegular(AcroFields form,PdfStamper stamper) {
131         if (dataMap == null || dataMap.size() == 0) {return;}
132         String key = "";
133         Iterator ekey = dataMap.keySet().iterator();
134         Object obj = null ;
135         while (ekey.hasNext()) {
136             key = ekey.next().toString();
137             try {
138                 obj = dataMap.get(key);
139                 if(obj instanceof List){
140                    //map中放的是list,為動態欄位
141                     dynData = (List)obj;
142                     transformDynTable(form);
143                 }else{
144                    //非空放入
145                     if( dataMap.get(key) != null) {
146                         if (Objects.equals(key,"code1") || Objects.equals(key,"code2") ){
147                             //key = code1或code2 進行生成條形碼;
148                             createBarCode(form,stamper,key,dataMap.get(key));
149                         }else if (Objects.equals(key,"qrCode")){
150                             //key = qrCode 進行生成二維碼
151                             createQrCode(form,stamper,key,dataMap.get(key));
152                         }else {
153                             form.setField(key, dataMap.get(key).toString());
154                         }
155                     }
156 
157                 }
158             } catch (Exception e) {
159                log.error("pdf賦值異常:{}",e.getMessage());
160             }
161         }
162     }
163 
164           /**
165           * 動態table的填充
166        * @param form
167        */
168     private void transformDynTable(AcroFields form) {
169         if (dynData == null || dynData.size() == 0)
170         {return;}
171         Object obj = null;
172         String name = "";
173         String value = "";
174         for (int x = 0; x < dynData.size(); x++) {
175             obj = dynData.get(x);
176             Field[] fld = obj.getClass().getDeclaredFields();
177             for (int i = 0; i < fld.length; i++) {
178                 name = fld[i].getName();
179                 value = (String) ReflectUtils.getFieldValue(obj, name);
180                 try {
181                     form.setField(name + x, value);
182                 } catch (IOException e) {
183                     e.printStackTrace();
184                 } catch (DocumentException e) {
185                     e.printStackTrace();
186                 }
187             }
188         }
189     }
190 
191     /**
192        * 對生成的pdf檔案進行後處理
193        *
194        * @throws RptException
195        */
196     private synchronized void postProcess() throws Exception {
197         FileOutputStream fosRslt = null;
198         PdfStamper stamper = null;
199         PdfReader reader = null;
200         try {
201             reader = new PdfReader(cachePath + File.separator + cacheFileName);
202             String save = savePath+File.separator+resultFileName;
203             File file = new File(save);
204             File parentFile = file.getParentFile();
205             if (!parentFile.exists()){
206                 parentFile.mkdirs();
207             }
208             fosRslt = new FileOutputStream(savePath + File.separator + resultFileName);
209             stamper = new PdfStamper(reader, fosRslt);
210 
211             Rectangle pageSize = reader.getPageSize(1);
212             float width = pageSize.getWidth();
213             BaseFont bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H",BaseFont.NOT_EMBEDDED);
214             PdfContentByte over;
215             int total = reader.getNumberOfPages() + 1;
216             for (int i = 1; i < total; i++) {
217                 over = stamper.getOverContent(i);
218                 if (total <= 2){break;}
219                 over.beginText();
220                 over.setFontAndSize(bf, 10);
221                 over.setTextMatrix(width - 92f, 32);
222                 over.showText("第 " + i + " 頁");
223                 over.endText();
224             }
225         } catch (Exception ie) {
226             ie.printStackTrace();
227         } finally {
228             if (stamper != null) {
229                 try {
230                     stamper.close();
231                 } catch (DocumentException e) {
232                     e.printStackTrace();
233                 } catch (IOException e) {
234                     e.printStackTrace();
235                 }
236             }
237             if (fosRslt != null) {
238                 try { fosRslt.close();
239                 } catch (IOException e) {
240                     e.printStackTrace();
241                 }
242             }
243             if (reader != null) {
244                 reader.close();
245             }
246             File pdfFile = new File(cachePath+File.separator + cacheFileName);
247             pdfFile.delete();
248         }
249 
250     }
251 
252     /**
253      * PDF中繪製條形碼
254      * @param form
255      * @param stamper
256      * @param key
257      * @param value
258      */
259     public void createBarCode(AcroFields form, PdfStamper stamper,String key,Object value){
260 
261         // 獲取屬性的類
262         if (value != null && form.getField(key) != null) {
263             //獲取位置(左上右下)
264             AcroFields.FieldPosition fieldPosition = form.getFieldPositions(key).get(0);
265             //繪製條碼
266             Barcode128 barcode128 = new Barcode128();
267             //字號
268             barcode128.setSize(6);
269             //條碼高度
270             if (key.equals("code1")){
271                 barcode128.setBarHeight(19.88f);
272                 barcode128.setBaseline(9);
273             }else {
274                 barcode128.setBarHeight(16.24f);
275                 //條碼與數字間距
276                 barcode128.setBaseline(8);
277             }
278             //條碼值
279             barcode128.setCode(value.toString());
280             barcode128.setStartStopText(false);
281             barcode128.setExtended(true);
282             //繪製在第一頁
283             PdfContentByte cb = stamper.getOverContent(1);
284             //生成條碼圖片
285             Image image128 = barcode128.createImageWithBarcode(cb, null, null);
286             //條碼位置
287             float marginLeft = (fieldPosition.position.getRight() - fieldPosition.position.getLeft() - image128.getWidth()) / 2;
288             if (key.equals("code2")){
289                 //條碼位置
290                 image128.setAbsolutePosition(fieldPosition.position.getLeft() + marginLeft, 395.67f);
291             }else {
292                 image128.setAbsolutePosition(fieldPosition.position.getLeft() + marginLeft, 157.8f );
293             }
294             //加入條碼
295             try {
296                 cb.addImage(image128);
297             } catch (DocumentException e) {
298              log.error("建立條碼異常:{}",e.getMessage());
299             }
300 
301         }
302 
303     }
304 
305     /**
306      * 繪製二維碼
307      * @param form
308      * @param stamper
309      * @param key
310      * @param value
311      */
312     public void createQrCode(AcroFields form, PdfStamper stamper,String key,Object value){
313         // 獲取屬性的型別
314         if(value != null && form.getField(key) != null){
315             //獲取位置(左上右下)
316             AcroFields.FieldPosition fieldPosition = form.getFieldPositions(key).get(0);
317             //繪製二維碼
318             float width = fieldPosition.position.getRight() - fieldPosition.position.getLeft();
319             BarcodeQRCode pdf417 = new BarcodeQRCode(value.toString(), (int)width, (int)width, null);
320             //生成二維碼影象
321             Image image128 = null;
322             try {
323                 image128 = pdf417.getImage();
324             } catch (BadElementException e) {
325                 log.error("建立二維碼異常:{}",e.getMessage());
326             }
327             //繪製在第一頁
328             PdfContentByte cb = stamper.getOverContent(1);
329             //左邊距(居中處理)
330             float marginLeft = (fieldPosition.position.getRight() - fieldPosition.position.getLeft() - image128.getWidth()) / 2;
331             //二維碼位置
332             image128.setAbsolutePosition(fieldPosition.position.getLeft() + marginLeft, 300);
333             image128.setBorderWidth(1000);
334             //加入二維碼
335             try {
336                 cb.addImage(image128);
337             } catch (DocumentException e) {
338                 log.error("加入二維碼異常:{}",e.getMessage());
339             }
340         }
341     }
342 
343 }
import lombok.extern.log4j.Log4j2;
import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipOutputStream;

import java.io.*;

/**
 * @description: 檔案打ZIP包工具類
 * @author: ZhuCJ  
 * @date: 2020-05-27 14:43
 */
@Log4j2
public class CompressZipUtil {

    /**
     * 壓縮檔案(資料夾)
     * @param path   目標檔案流
     * @param format zip 格式 | rar 格式
     * @throws Exception
     */
    public static String zipFile(File path, String format) throws Exception {
        String generatePath = "";
        if (path.isDirectory()) {
            generatePath = path.getParent().endsWith(File.separator) == false ?
                    path.getParent() + File.separator + path.getName() + "." + format :
                    path.getParent() + path.getName() + "." + format;
        } else {
            generatePath = path.getParent().endsWith(File.separator) == false ? path.getParent() + File.separator :
                    path.getParent();
            generatePath += path.getName().substring(0, path.getName().lastIndexOf(".")) + "." + format;
        }
        // 輸出流
        FileOutputStream outputStream = new FileOutputStream(generatePath);
        // 壓縮輸出流
        ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(outputStream));
        zip(out, path, "");
        out.flush();
        out.close();

        return generatePath;
    }

    /**
     * @param sourcePath 要壓縮的檔案路徑
     * @param suffix     生成的格式後最(zip、rar)
     */
    public static void generateFile(String sourcePath, String suffix) throws Exception {

        File file = new File(sourcePath);
        // 壓縮檔案的路徑不存在
        if (!file.exists()) {
            throw new Exception("路徑 " + sourcePath + " 不存在檔案,無法進行壓縮...");
        }
        // 用於存放壓縮檔案的資料夾
        String generateFile = file.getParent() + File.separator + "CompressFile";
        File compress = new File(generateFile);
        // 如果資料夾不存在,進行建立
        if (!compress.exists()) {
            compress.mkdirs();
        }
        // 目的壓縮檔案
        String generateFileName = compress.getAbsolutePath() + File.separator + "AAA" + file.getName() + "." + suffix;

        // 輸入流 表示從一個源讀取資料
        // 輸出流 表示向一個目標寫入資料

        // 輸出流
        FileOutputStream outputStream = new FileOutputStream(generateFileName);

        // 壓縮輸出流
        ZipOutputStream zipOutputStream = new ZipOutputStream(new BufferedOutputStream(outputStream));

        generateFile(zipOutputStream, file, "");

        System.out.println("原始檔位置:" + file.getAbsolutePath() + ",目的壓縮檔案生成位置:" + generateFileName);
        // 關閉 輸出流
        zipOutputStream.close();
    }

    /**
     * @param out  輸出流
     * @param file 目標檔案
     * @param dir  資料夾
     * @throws Exception
     */
    private static void generateFile(ZipOutputStream out, File file, String dir) {
        FileInputStream inputStream = null;
        try {
            // 當前的是資料夾,則進行一步處理
            if (file.isDirectory()) {
                //得到檔案列表資訊
                File[] files = file.listFiles();

                //將資料夾新增到下一級打包目錄
                out.putNextEntry(new ZipEntry(dir + File.separator));

                dir = dir.length() == 0 ? "" : dir + File.separator;

                //迴圈將資料夾中的檔案打包
                for (int i = 0; i < files.length; i++) {
                    generateFile(out, files[i], dir + files[i].getName());
                }

            } else { // 當前是檔案

                // 輸入流
                inputStream = new FileInputStream(file);
                // 標記要打包的條目
                out.putNextEntry(new ZipEntry(dir));
                // 進行寫操作
                int len = 0;
                byte[] bytes = new byte[1024];
                while ((len = inputStream.read(bytes)) > 0) {
                    out.write(bytes, 0, len);
                }

            }
        } catch (Exception e) {
            log.error("generateFile異常:", e);
        } finally {
            // 關閉輸入流
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }


    }

    /**
     * 遞迴壓縮檔案
     *
     * @param output    ZipOutputStream 物件流
     * @param file      壓縮的目標檔案流
     * @param childPath 條目目錄
     */
    private static void zip(ZipOutputStream output, File file, String childPath) {
        FileInputStream input = null;
        try {
            // 檔案為目錄
            if (file.isDirectory()) {
                // 得到當前目錄裡面的檔案列表
                File list[] = file.listFiles();
                childPath = childPath + (childPath.length() == 0 ? "" : File.separator)
                        + file.getName();
                // 迴圈遞迴壓縮每個檔案
                for (File f : list) {
                    zip(output, f, childPath);
                }
            } else {
                // 壓縮檔案
                childPath = (childPath.length() == 0 ? "" : childPath + File.separator)
                        + file.getName();
                output.putNextEntry(new ZipEntry(childPath));
                input = new FileInputStream(file);
                int readLen = 0;
                byte[] buffer = new byte[1024 * 8];
                while ((readLen = input.read(buffer, 0, 1024 * 8)) != -1) {
                    output.write(buffer, 0, readLen);
                }
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            // 關閉流
            if (input != null) {
                try {
                    input.close();
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
        }

    }

}
import lombok.extern.slf4j.Slf4j;

import java.io.File;

/**
 * @description: 刪除指定檔案或資料夾下所有內容
 * @author: ZhuCJ 
 * @date: 2020-06-02 0:18
 */
@Slf4j
public class DeleteFileUtil {

    public static void deleteFile(File file){
        //取得這個目錄下的所有子檔案物件
        File[] files = file.listFiles();
        //遍歷該目錄下的檔案物件
        for (File f: files){
            //判斷子目錄是否存在子目錄,如果是檔案則刪除
            if (f.isDirectory()){
                deleteFile(f);
            }else {
                f.delete();
            }
        }
        //刪除空資料夾  for迴圈已經把上一層節點的目錄清空。
        file.delete();
    }
}
import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import java.lang.reflect.*;
import java.util.Date;

/**
 * @description: 反射設定物件屬性值
 * @author: ZhuCJ
 * @date: 2020-05-27 11:16
 */
public class ReflectUtils {

    private static final String SETTER_PREFIX = "set";

    private static final String GETTER_PREFIX = "get";

    private static final String CGLIB_CLASS_SEPARATOR = "$$";

    private static Logger logger = LoggerFactory.getLogger(ReflectUtils.class);

    /**
     * 呼叫Getter方法.
     * 支援多級,如:物件名.物件名.方法
     */
    public static Object invokeGetter(Object obj, String propertyName) {
        Object object = obj;
        for (String name : StringUtils.split(propertyName, ".")){
            String getterMethodName = GETTER_PREFIX + org.springframework.util.StringUtils.capitalize(name);
            object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {});
        }
        return object;
    }

    /**
     * 呼叫Setter方法, 僅匹配方法名。
     * 支援多級,如:物件名.物件名.方法
     */
    public static void invokeSetter(Object obj, String propertyName, Object value) {
        Object object = obj;
        String[] names = StringUtils.split(propertyName, ".");
        for (int i=0; i<names.length; i++){
            if(i<names.length-1){
                String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]);
                object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {});
            }else{
                String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]);
                invokeMethodByName(object, setterMethodName, new Object[] { value });
            }
        }
    }

    /**
     * 直接讀取物件屬性值, 無視private/protected修飾符, 不經過getter函式.
     */
    public static Object getFieldValue(final Object obj, final String fieldName) {
        Field field = getAccessibleField(obj, fieldName);

        if (field == null) {
            throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + obj + "]");
        }

        Object result = null;
        try {
            result = field.get(obj);
        } catch (IllegalAccessException e) {
            logger.error("不可能丟擲的異常{}", e.getMessage());
        }
        return result;
    }

    /**
     * 直接設定物件屬性值, 無視private/protected修飾符, 不經過setter函式.
     */
    public static void setFieldValue(final Object obj, final String fieldName, final Object value) {
        Field field = getAccessibleField(obj, fieldName);

        if (field == null) {
            throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + obj + "]");
        }

        try {
            field.set(obj, value);
        } catch (IllegalAccessException e) {
            logger.error("不可能丟擲的異常:{}", e.getMessage());
        }
    }

    /**
     * 直接呼叫物件方法, 無視private/protected修飾符.
     * 用於一次性呼叫的情況,否則應使用getAccessibleMethod()函式獲得Method後反覆呼叫.
     * 同時匹配方法名+引數型別,
     */
    public static Object invokeMethod(final Object obj, final String methodName, final Class<?>[] parameterTypes,
                                      final Object[] args) {
        Method method = getAccessibleMethod(obj, methodName, parameterTypes);
        if (method == null) {
            throw new IllegalArgumentException("Could not find method [" + methodName + "] on target [" + obj + "]");
        }

        try {
            return method.invoke(obj, args);
        } catch (Exception e) {
            throw convertReflectionExceptionToUnchecked(e);
        }
    }

    /**
     * 直接呼叫物件方法, 無視private/protected修飾符,
     * 用於一次性呼叫的情況,否則應使用getAccessibleMethodByName()函式獲得Method後反覆呼叫.
     * 只匹配函式名,如果有多個同名函式呼叫第一個。
     */
    public static Object invokeMethodByName(final Object obj, final String methodName, final Object[] args) {
        Method method = getAccessibleMethodByName(obj, methodName);
        if (method == null) {
            throw new IllegalArgumentException("Could not find method [" + methodName + "] on target [" + obj + "]");
        }

        try {
            return method.invoke(obj, args);
        } catch (Exception e) {
            throw convertReflectionExceptionToUnchecked(e);
        }
    }

    /**
     * 迴圈向上轉型, 獲取物件的DeclaredField, 並強制設定為可訪問.
     *
     * 如向上轉型到Object仍無法找到, 返回null.
     */
    public static Field getAccessibleField(final Object obj, final String fieldName) {
        Validate.notNull(obj, "object can't be null");
        Validate.notBlank(fieldName, "fieldName can't be blank");
        for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) {
            try {
                Field field = superClass.getDeclaredField(fieldName);
                makeAccessible(field);
                return field;
            } catch (NoSuchFieldException e) {//NOSONAR
                // Field不在當前類定義,繼續向上轉型
                continue;// new add
            }
        }
        return null;
    }

    /**
     * 迴圈向上轉型, 獲取物件的DeclaredMethod,並強制設定為可訪問.
     * 如向上轉型到Object仍無法找到, 返回null.
     * 匹配函式名+引數型別。
     *
     * 用於方法需要被多次呼叫的情況. 先使用本函式先取得Method,然後呼叫Method.invoke(Object obj, Object... args)
     */
    public static Method getAccessibleMethod(final Object obj, final String methodName,
                                             final Class<?>... parameterTypes) {
        Validate.notNull(obj, "object can't be null");
        Validate.notBlank(methodName, "methodName can't be blank");

        for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) {
            try {
                Method method = searchType.getDeclaredMethod(methodName, parameterTypes);
                makeAccessible(method);
                return method;
            } catch (NoSuchMethodException e) {
                // Method不在當前類定義,繼續向上轉型
                continue;// new add
            }
        }
        return null;
    }

    /**
     * 迴圈向上轉型, 獲取物件的DeclaredMethod,並強制設定為可訪問.
     * 如向上轉型到Object仍無法找到, 返回null.
     * 只匹配函式名。
     *
     * 用於方法需要被多次呼叫的情況. 先使用本函式先取得Method,然後呼叫Method.invoke(Object obj, Object... args)
     */
    public static Method getAccessibleMethodByName(final Object obj, final String methodName) {
        Validate.notNull(obj, "object can't be null");
        Validate.notBlank(methodName, "methodName can't be blank");

        for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) {
            Method[] methods = searchType.getDeclaredMethods();
            for (Method method : methods) {
                if (method.getName().equals(methodName)) {
                    makeAccessible(method);
                    return method;
                }
            }
        }
        return null;
    }

    /**
     * 改變private/protected的方法為public,儘量不呼叫實際改動的語句,避免JDK的SecurityManager抱怨。
     */
    public static void makeAccessible(Method method) {
        if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers()))
                && !method.isAccessible()) {
            method.setAccessible(true);
        }
    }

    /**
     * 改變private/protected的成員變數為public,儘量不呼叫實際改動的語句,避免JDK的SecurityManager抱怨。
     */
    public static void makeAccessible(Field field) {
        if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) || Modifier
                .isFinal(field.getModifiers())) && !field.isAccessible()) {
            field.setAccessible(true);
        }
    }

    /**
     * 通過反射, 獲得Class定義中宣告的泛型引數的型別, 注意泛型必須定義在父類處
     * 如無法找到, 返回Object.class.
     * eg.
     * public UserDao extends HibernateDao<User>
     *
     * @param clazz The class to introspect
     * @return the first generic declaration, or Object.class if cannot be determined
     */
    @SuppressWarnings("unchecked")
    public static <T> Class<T> getClassGenricType(final Class clazz) {
        return getClassGenricType(clazz, 0);
    }

    /**
     * 通過反射, 獲得Class定義中宣告的父類的泛型引數的型別.
     * 如無法找到, 返回Object.class.
     *
     * 如public UserDao extends HibernateDao<User,Long>
     *
     * @param clazz clazz The class to introspect
     * @param index the Index of the generic ddeclaration,start from 0.
     * @return the index generic declaration, or Object.class if cannot be determined
     */
    public static Class getClassGenricType(final Class clazz, final int index) {

        Type genType = clazz.getGenericSuperclass();

        if (!(genType instanceof ParameterizedType)) {
            logger.warn(clazz.getSimpleName() + "'s superclass not ParameterizedType");
            return Object.class;
        }

        Type[] params = ((ParameterizedType) genType).getActualTypeArguments();

        if (index >= params.length || index < 0) {
            logger.warn("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: "
                    + params.length);
            return Object.class;
        }
        if (!(params[index] instanceof Class)) {
            logger.warn(clazz.getSimpleName() + " not set the actual class on superclass generic parameter");
            return Object.class;
        }

        return (Class) params[index];
    }

    public static Class<?> getUserClass(Object instance) {
        Validate.notNull(instance, "Instance must not be null");
        Class clazz = instance.getClass();
        if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) {
            Class<?> superClass = clazz.getSuperclass();
            if (superClass != null && !Object.class.equals(superClass)) {
                return superClass;
            }
        }
        return clazz;

    }

    /**
     * 將反射時的checked exception轉換為unchecked exception.
     */
    public static RuntimeException convertReflectionExceptionToUnchecked(Exception e) {
        if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException
                || e instanceof NoSuchMethodException) {
            return new IllegalArgumentException(e);
        } else if (e instanceof InvocationTargetException) {
            return new RuntimeException(((InvocationTargetException) e).getTargetException());
        } else if (e instanceof RuntimeException) {
            return (RuntimeException) e;
        }
        return new RuntimeException("Unexpected Checked Exception.", e);
    }

    /**
     * 判斷屬性是否為日期型別
     *
     * @param clazz
     *            資料型別
     * @param fieldName
     *            屬性名
     * @return 如果為日期型別返回true,否則返回false
     */
    public static <T> boolean isDateType(Class<T> clazz, String fieldName) {
        boolean flag = false;
        try {
            Field field = clazz.getDeclaredField(fieldName);
            Object typeObj = field.getType().newInstance();
            flag = typeObj instanceof Date;
        } catch (Exception e) {
            // 把異常吞掉直接返回false
        }
        return flag;
    }

    /**
     * 根據型別將指定引數轉換成對應的型別
     *
     * @param value
     *            指定引數
     * @param type
     *            指定型別
     * @return 返回型別轉換後的物件
     */
    public static <T> Object parseValueWithType(String value, Class<?> type) {
        Object result = null;
        try { // 根據屬性的型別將內容轉換成對應的型別
            if (Boolean.TYPE == type) {
                result = Boolean.parseBoolean(value);
            } else if (Byte.TYPE == type) {
                result = Byte.parseByte(value);
            } else if (Short.TYPE == type) {
                result = Short.parseShort(value);
            } else if (Integer.TYPE == type) {
                result = Integer.parseInt(value);
            } else if (Long.TYPE == type) {
                result = Long.parseLong(value);
            } else if (Float.TYPE == type) {
                result = Float.parseFloat(value);
            } else if (Double.TYPE == type) {
                result = Double.parseDouble(value);
            } else {
                result = (Object) value;
            }
        } catch (Exception e) {
            // 把異常吞掉直接返回null
        }
        return result;
    }
}

4.使用的maven依賴

<!--檔案轉成PDF-->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.13</version>
</dependency>
<!--打成zip壓縮包-->
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant</artifactId>
<version>1.10.5</version>
</dependency>