下載服務端數據到本地保存爲Excel

引言

​ 在項目中,往往會需要將查詢出的數據導出成excel或者其他的文件形式,便於用戶查看。因此就有了這篇博文,介紹如何下載服務器的數據並以excel的形式保存到本地。

整體思路

  • 查詢出目標數據,並生成對應的文件格式的文件。在本項目就是以Excel格式存儲查詢出的數據。
  • 文件可以保存在服務器上,也可以直接以文件流的形式寫入到Response的輸出流中。本文分成兩步,先保存到服務器,然後寫入響應的輸出流。建議直接寫入Response,先寫成文件再輸出在併發時會導致阻塞。
  • 設置響應頭Header,在瀏覽器訪問時,彈出打開/下載彈窗。

代碼實現

  1. ajax查詢數據並將數據以Excel形式保存到服務器

    //項目採用struts框架,因此分兩步走
    public ActionForward exportData(ActionMapping mapping,
            ActionForm form, HttpServletRequest request,
            HttpServletResponse response) throws IOException {
        int fileId = Integer.parseInt(request.getParameter("id"));
        Software software = (Software)softwareDao.getById(Software.class, fileId);
        //將文件名中的空格去除
            String excelName = FileTools.formatFileName(software.getName());
        List<Map<String, Object>> successInfo = recordDao.getSuccessInfo(fileId);
        List<Map<String, Object>> failedInfo = recordDao.getFailedInfo(fileId);
        String successKey = "成功記錄";
        String failedKey = "失敗記錄";
        //待導出的數據
            Map<String, List<Map<String, Object>>> data = new HashMap<>();
        data.put(successKey, successInfo);
        data.put(failedKey, failedInfo);
        String serverPath = request.getSession().getServletContext().getRealPath("/");
        String excelPath = serverPath+"download/"+excelName+".xls";
        JSONObject object = new JSONObject();
        try {
                //使用poi導出數據
            ExcelUtil.exportData(excelPath, data, successKey,failedKey);
            //加密文件路徑
                String cryptPath = AESCrpyt.encryptString(excelPath, AESCrpyt.DEFAULT_KEY);
            object.put("code", 0);
            object.put("msg", cryptPath);
        } catch(IndexOutOfBoundsException e){
            object.put("code", -1);
            object.put("msg", "沒有相關升級記錄,導出失敗");
        } catch (Exception e) {
            log.error("導出失敗");
            log.error(e);
            object.put("code", -1);
            object.put("msg", "導出失敗");
        }
        response.setCharacterEncoding(ProductConfig.CHARSET);
        try {
            response.getWriter().write(object.toString());
            response.getWriter().flush();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            log.error("信息輸出失敗");
        }
           return null;
    }
    /**
        * 輸出數據到EXCEL
        * @param excelPath      輸出Excel路徑
        * @param data           輸出數據
        * @param excelNames Excel文件中的多個表
        * @throws Exception 導出時異常
        */
    public static void exportData(String excelPath,
            Map<String, List<Map<String, Object>>>data,
            String...excelNames) throws Exception{
        //檢查數據中表的表名和參數表名是否一致
        checkData(data, excelNames);
        //檢查導出路徑
        validatePath(excelPath);
        HSSFWorkbook wb = new HSSFWorkbook();
        for (String excelName : excelNames) {
            List<Map<String, Object>> list = data.get(excelName);
            HSSFSheet sheet = wb.createSheet(excelName);  
            HSSFRow row = sheet.createRow((int) 0);  
            HSSFCellStyle style = wb.createCellStyle();  
            style.setAlignment(HorizontalAlignment.CENTER);
            HSSFCell cell;
    
            Map<String, Object> item = list.get(0);
            Set<String> keys = item.keySet();
    
            Iterator<String> i = keys.iterator();
            int num = 0;
            while (i.hasNext()) {
                String firstLine = i.next();
                cell = row.createCell((short)num);
                cell.setCellValue(firstLine);
                cell.setCellStyle(style);  
                num++;
            }
            for (int j = 0; j < list.size(); j++)  
            { 
                row = sheet.createRow((int) j + 1);  
                Map<String, Object> itemMap = list.get(j);
                Set<String> keySet = itemMap.keySet();
                Iterator<String> k = keySet.iterator();
                int rowNum = 0;
                while (k.hasNext()) {
                    String firstLine = k.next();
                    cell = row.createCell((short)rowNum);
                    cell.setCellValue(itemMap.get(firstLine)==null?"":itemMap.get(firstLine).toString()); 
                    rowNum++;
                }
            }
        } 
           FileOutputStream fout = new FileOutputStream(excelPath);  
           wb.write(fout);  
           fout.close();
           fout = null;
    
    }
    //AES加密字符串
    public class AESCrpyt {
    
    public static final String DEFAULT_KEY = "defaultkey4crypt";
    
    private static String TYPE = "AES";
    
    private static int KeySizeAES128 = 16;
    
    private static int BUFFER_SIZE = 8192;
    
    public static final String VIPARA = "0102030405060708";
    
    private static Cipher getCipher(String key, int mode) throws Exception, InvalidAlgorithmParameterException {
    
        // mode =Cipher.DECRYPT_MODE or Cipher.ENCRYPT_MODE
    
        Cipher mCipher;
    
        byte[] keyPtr = new byte[KeySizeAES128];
    
        IvParameterSpec IvParameterSpecivParam = new IvParameterSpec(VIPARA.getBytes());
    
        byte[] passPtr = key.getBytes();
    
        mCipher = Cipher.getInstance(TYPE + "/CBC/PKCS5Padding");
    
        for (int i = 0; i < KeySizeAES128; i++) {
    
            if (i < passPtr.length)
                keyPtr[i] = passPtr[i];
    
            else
                keyPtr[i] = 0;
    
        }
    
        SecretKeySpec keySpec = new SecretKeySpec(keyPtr, TYPE);
    
        mCipher.init(mode, keySpec, IvParameterSpecivParam);
    
        return mCipher;
    
    }
    
     public static String encryptString(String targetString,String encryptKey)throws Exception{
        String result="";
        Cipher enCipher = getCipher(encryptKey, Cipher.ENCRYPT_MODE);
        if(enCipher==null){
            throw new Exception("encrypt init failed");
        }   
        result =parseByte2HexStr(enCipher.doFinal(targetString.getBytes()));
        return result;
    }
    
    public static String decryptString(String encryptString,String encryptKey) throws         Exception{
        String result=null;
        Cipher enCipher = getCipher(encryptKey, Cipher.DECRYPT_MODE);
        if(enCipher==null){
            throw new Exception("decrypt init failed");
        }
        result = new String(enCipher.doFinal(parseHexStr2Byte(encryptString)));
        return result;
    }
    
    private static String parseByte2HexStr(byte buf[]) {  
           StringBuffer sb = new StringBuffer();  
           for (int i = 0; i < buf.length; i++) {  
                   String hex = Integer.toHexString(buf[i] & 0xFF);  
                   if (hex.length() == 1) {  
                           hex = '0' + hex;  
                   }  
                   sb.append(hex.toUpperCase());  
           }  
           return sb.toString();  
    } 
    
    private static byte[] parseHexStr2Byte(String hexStr) {  
           if (hexStr.length() < 1)  
                   return null;  
           byte[] result = new byte[hexStr.length()/2];  
           for (int i = 0;i< hexStr.length()/2; i++) {  
                   int high = Integer.parseInt(hexStr.substring(i*2, i*2+1), 16);  
                   int low = Integer.parseInt(hexStr.substring(i*2+1, i*2+2), 16);  
                   result[i] = (byte) (high * 16 + low);  
           }  
           return result;  
    } 
    }
    //ajax查詢數據,如果查詢成功並生成excel,則轉到servlet進行下載。
    $("i").bind("click",function(){
                    var id = $(this).parent().siblings(":first").text();
                    $.ajax({
                        type:"POST",
                        url:"software.do",
                        dataType:"json",
                        data:{
                                method:"exportData",
                                id:id
                                },
                        success:function(data)
                            {
                                var code = data.code;
                                if(code==0){
                                    var path = data.msg;
                                    //此處加密過的路徑可能會超出url規定的最大長度
                                    window.location.href='ExportExcel?path='+path; 
                                } else {
                                    layer.alert(data.msg);
                                }
                            }
                    });
                });
  2. 轉到一個新的Servlet:ExportExcel 將Excel文件寫入到Response輸出流。

    package com.bydota.product.servlet;
    
    import java.io.BufferedOutputStream;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.OutputStream;
    import java.net.URLEncoder;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpUtils;
    
    import com.bydota.product.tools.AESCrpyt;
    import com.bydota.product.tools.FileTools;
    
    /**
    * Servlet implementation class ExportExcel
    */
    public class ExportExcel extends HttpServlet {
    private static final long serialVersionUID = 1L;
    
       /**
        * @see HttpServlet#HttpServlet()
        */
       public ExportExcel() {
           super();
           // TODO Auto-generated constructor stub
       }
    
    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
        String cryptPath = request.getParameter("path");
        String excelPath = "";
        try {
            excelPath = AESCrpyt.decryptString(cryptPath, AESCrpyt.DEFAULT_KEY);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        //截取文件名
        String excelName = FileTools.getFileName(excelPath);
        response.reset();  
        //下載文件格式
        response.setHeader("Content-Type", "application/vnd.ms-excel");
        //防止下載時出現的中文亂碼
        //即filename*=charset'lang'value。charset則是給瀏覽器指明以什麼編碼方式來還原中文文件名。 value爲編碼後的元數據
           String ua = request.getHeader("User-Agent");
        System.out.println(ua);
        //兼容問題
        if(ua.contains("MSIE")){
            response.setHeader("Content-Disposition","attachment;filename="+ URLEncoder.encode(excelName,"UTF-8")); 
        } else {
            response.setHeader("Content-Disposition","attachment;filename*=UTF-8''"+ URLEncoder.encode(excelName,"UTF-8"));//指定下載的文件名  
        }
    
           //此處必須設置無緩存,否則IE中會出問題
           response.setHeader("Pragma", "no-cache");  
           response.setHeader("Cache-Control", "no-cache");  
           response.setDateHeader("Expires", 0);  
           OutputStream output = response.getOutputStream();  
           BufferedOutputStream bufferedOutPut = new BufferedOutputStream(output);  
           bufferedOutPut.flush();  
           FileInputStream in = new FileInputStream(excelPath);
           byte[] b = new byte[1024];
           int length = 0;
           while((length = in.read(b))!=-1){
            bufferedOutPut.write(b, 0, length);
           }
           in.close();
           bufferedOutPut.close(); 
    }
    
    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
        doGet(request, response);
    }
    
    }
    

注意

  1. 響應頭Header最好採用filename*=charset’lang’value,以解決中文亂碼問題。
  2. 針對火狐,filename不能有空格,否則會出現下載文件不帶後綴的情況。
  3. 有關POI和可用的POI資源可以參考我的另一篇文章。因爲項目中是封裝好對map數據的處理,如果你想自己輸出Bean數據,那就要你重新封裝。

參考文章

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章