1. 程式人生 > >Java上傳下載excel、解析Excel、生成Excel的問題

Java上傳下載excel、解析Excel、生成Excel的問題

在軟體開發過程中難免需要批量上傳與下載,生成報表儲存也是常有之事,最近集團門戶開發用到了Excel模版下載,Excel生成,圓滿完成,對這一知識點進行整理,資源共享,有不足之處還望批評指正,文章結尾提供了所需jar包的下載,方便大夥使用,下面言歸正傳!

    1.Excel的下載

      1)Action中:

         新增響應事件,通過getRealPath獲得工程路徑,與jsp中獲得request.getContextPath()效果相同,fileName為要下載的檔名,經過拼接filePath是xls檔案的絕對路徑,呼叫工具類DownLoadUtil中的downLoadUtil方法;

        public ActionForward downLoadExcelModel(ActionMapping actionMapping,
           ActionForm actionForm, HttpServletRequest request,
           HttpServletResponse response) throws Exception {
           String path = request.getSession().getServletContext().getRealPath(
            "/resources");
           String fileName = "成員模版.xls";
           String filePath = path + "\\" + fileName;
           DownLoadUtil.downLoadFile(filePath, response, fileName, "xls");
           return null;
        }

      2)工具類DownLoadUtil中:

     public static boolean downLoadFile(String filePath,
        HttpServletResponse response, String fileName, String fileType)
        throws Exception {
        File file = new File(filePath);  //根據檔案路徑獲得File檔案
        //設定檔案型別(這樣設定就不止是下Excel檔案了,一舉多得)
        if("pdf".equals(fileType)){
           response.setContentType("application/pdf;charset=GBK");
        }else if("xls".equals(fileType)){
           response.setContentType("application/msexcel;charset=GBK");
        }else if("doc".equals(fileType)){
           response.setContentType("application/msword;charset=GBK");
        }

        //檔名
        response.setHeader("Content-Disposition", "attachment;filename=\""
            + new String(fileName.getBytes(), "ISO8859-1") + "\"");
        response.setContentLength((int) file.length());
        byte[] buffer = new byte[4096];// 緩衝區
        BufferedOutputStream output = null;
        BufferedInputStream input = null;
        try {
          output = new BufferedOutputStream(response.getOutputStream());
          input = new BufferedInputStream(new FileInputStream(file));
          int n = -1;

          //遍歷,開始下載
          while ((n = input.read(buffer, 0, 4096)) > -1) {
             output.write(buffer, 0, n);
          }
          output.flush();   //不可少
          response.flushBuffer();//不可少
        } catch (Exception e) {
          //異常自己捕捉       

        } finally {

           //關閉流,不可少
           if (input != null)
                input.close();
           if (output != null)
                output.close();
        }

       return false;
    }

    這樣,就可以完成檔案的下載,java程式將檔案以流的形式,儲存到客戶本機!!

    2、Excel的上傳與解析(此處方面起見,ExcelN行兩列,以為手機號,另一為短號)

    對於資料批量上傳,就涉及到檔案的上傳與資料的解析功能,此處運用了第三方jar包,方便快捷。對於檔案上傳,用到了commons-fileupload-1.1.1.jar與commons-io-1.1.jar,程式碼中並沒有匯入common-io包中的內容,而fielupload包的上傳依賴於common-io,所以兩者皆不可少;而對於Excel的解析,此處用到了jxl-2.6.jar。下面通過程式碼對各步驟進行解析。

     1)jsp檔案:

      對於檔案的上傳,強烈推薦使用form表單,並設定enctype="multipart/form-data" method="post",action為處理檔案的路徑

      <form id="fileUpload" action="excel.do?method=exportMemberExcel" enctype="multipart/form-data" method="post">
      <input id="excelFile" name="file" type="file"/>
      <input type="button" value="提交" onclick="submitExcel()"/>
  </form>

    2)js檔案:

      通過js檔案對上傳的檔案進行初識的格式驗證,判斷是否為空以及格式是否正確,正確的話提交表單,由後臺對上傳的檔案處理,此處用的是jQuery,需要匯入jquery-***.js,此處使用的是jquery-1.4.2.min.js(最後提供下載地址)。

    function submitExcel(){
       var excelFile = $("#excelFile").val();
       if(excelFile=='') {alert("請選擇需上傳的檔案!");return false;}
       if(excelFile.indexOf('.xls')==-1){alert("檔案格式不正確,請選擇正確的Excel檔案(字尾名.xls)!");return false;}
       $("#fileUpload").submit();
    }

    3)Action中:

     使用common-fileupload包中的類FileItemFactory,ServletFileUpload對請求進行處理,如果是檔案,用工具累ExcelUtil處理,不是檔案,此處未處理,不過以後開發可以根據需要處理,此處不纍述。(因為如果Excel中如果存在錯誤記錄,還需供使用者下載,所以若有錯誤資訊,暫儲存在session中,待使用者下載後可清空此session)。

      public ActionForward exportMemberExcel(ActionMapping actionMapping,
          ActionForm actionForm, HttpServletRequest request,
          HttpServletResponse response) throws Exception {
      int maxPostSize = 1000 * 1024 * 1024;
      FileItemFactory factory = new DiskFileItemFactory();

      ServletFileUpload servletFileUpload = new ServletFileUpload(factory);
      servletFileUpload.setSizeMax(maxPostSize);

      try {
      List fileItems = servletFileUpload.parseRequest(request);
      Iterator iter = fileItems.iterator();
      while (iter.hasNext()) {
          FileItem item = (FileItem) iter.next();
          if (!item.isFormField()) {// 是檔案
          Map map = ExcelUtil.excel2PhoneList(item.getInputStream());

     String[][] error = (String[][]) map.get("error");
     
     if (error.length != 0) {
      //如果存在錯誤,存入記憶體,供使用者儲存
      request.setAttribute("errorFlag", "ture");
      request.getSession().setAttribute("exportMap", map);
     } else {
      request.setAttribute("errorFalg", "false");
      // 獲取正確的值填寫json
      String phoneJson = JsonUtil
        .phoneArray2Json((String[][]) map
          .get("current"));
      request.setAttribute("phoneJson", phoneJson);
     }
    } else {    //不是檔案,此處不處理,對於text,password等文字框可在此處理
        logger.error("wrong file");
    }
   }
  } catch (Exception e) {
   logger.error("invoke fileUpload error.", e);
  }
  return actionMapping.findForward("queryAccounts");
 }
  
 4、工具類ExcelUtil

   主要藉助jxl包對Excel檔案進行解析,獲取正確資訊以及錯誤資訊,供使用者取捨。

   public static Map excel2PhoneList(InputStream inputStream) throws Exception {
     Map map = new HashMap();
     Workbook workbook = Workbook.getWorkbook(inputStream);  //處理輸入流
     Sheet sheet = workbook.getSheet(0);// 獲取第一個sheet
     int rows = sheet.getRows();   //獲取總行號
     String[][] curArr = new String[rows][2];     //存放正確心細
     String[][] errorArr = new String[rows * 2][4];   //存放錯誤資訊
     int curLines = 0;
     int errorLines = 0;
  for (int i = 1; i < rows; i++) {// 遍歷行獲得每行資訊

     String phone = sheet.getCell(0, i).getContents();// 獲得第i行第1列資訊

     String shortNum = sheet.getCell(1, i).getContents();// 短號
     StringBuffer errorMsg = new StringBuffer();
     if (!isRowEmpty(sheet, i)) { //此行不為空
        //對此行資訊進行正誤判斷           
     }
  }// 行

  //正誤資訊存入map,儲存
  map.put("current", current);
  map.put("error", error);
   return map;
 }

   private static boolean isRowEmpty(Sheet sheet, int i) {
    String phone = sheet.getCell(0, i).getContents();// 集團編號
    String shortNum = sheet.getCell(1, i).getContents();// 集團名稱
    if (isEmpty(phone) && isEmpty(shortNum))
        return true;
    return false;
   }

 返回值由Action處理,此處為止,Excel的解析就完成了!

  3 生成Excel

  上文中說到如果存在錯誤資訊,可供使用者下載,錯誤資訊存在Session中,下載就需要利用Session中的資料生成Excel文件,供使用者儲存。

   1)Action中:

   Action從Session中獲取儲存的物件,呼叫工具類儲存,然後刪除Session中的內容.

   public ActionForward downErrorExcel(ActionMapping actionMapping,
      ActionForm actionForm, HttpServletRequest request,
      HttpServletResponse response) throws Exception {
     Map map = (Map) request.getSession().getAttribute("exportMap");
     ExcelUtil.createExcel(response, map);
     request.getSession.removeAttribure("exportMap");
     return null;
    }

 2)工具類ExcelUtil中:

     public static boolean createExcel(HttpServletResponse response, Map map) {
     WritableWorkbook wbook = null;
     WritableSheet sheet = null;
     OutputStream os = null;
     try {
       response.reset();   

       //生成檔名
       response.setHeader("Content-disposition", "attachment; filename="
           + new String("錯誤資訊".getBytes("GB2312"), "ISO8859-1")
           + ".xls");
       response.setContentType("application/msexcel");
       os = response.getOutputStream();
       wbook = Workbook.createWorkbook(os);
       sheet = wbook.createSheet("資訊", 0);

       int row = 0;

       //填寫表頭
       setSheetTitle(sheet, row);

       //遍歷map,填充資料
       setSheetData(sheet, map, row);

       wbook.write();
       os.flush();
     } catch (Exception e) {
       //自己處理
     }finally {
         try {//切記,此處一定要關閉流,否則你會下載一個空檔案
          if (wbook != null)
              wbook.close();
          if (os != null)
              os.close();
         } catch (IOException e) {
            e.printStackTrace();
         } catch (WriteException e) {
            e.printStackTrace();
      }
  }
  return false;
 }

private static void setSheetTitle(WritableSheet sheet, int row)
   throws RowsExceededException, WriteException {
  // 設定excel標題格式
  WritableFont wfont = new WritableFont(WritableFont.ARIAL, 12,
    WritableFont.BOLD, false, UnderlineStyle.NO_UNDERLINE,
    Colour.BLACK);
  WritableCellFormat wcfFC = new WritableCellFormat(wfont);

  //設定每一列寬度
  sheet.setColumnView(0,13);
  sheet.setColumnView(1,13);
  sheet.setColumnView(2,13);
  sheet.setColumnView(3,50);

  //填寫第一行提示資訊

  sheet.addCell(new Label(0, row, "手機號碼", wcfFC));
  sheet.addCell(new Label(1, row, "短號", wcfFC));
  sheet.addCell(new Label(2, row, "原錯誤行號", wcfFC));
  sheet.addCell(new Label(3, row, "錯誤原因", wcfFC));
 }

 private static void setSheetData(WritableSheet sheet, Map map, int row)
   throws RowsExceededException, WriteException {
  WritableFont wfont = new WritableFont(WritableFont.ARIAL, 10,
    WritableFont.NO_BOLD, false, UnderlineStyle.NO_UNDERLINE,
    Colour.RED);
  WritableCellFormat wcfFC = new WritableCellFormat(wfont);

  //從map中獲取資料
  String[][] error = (String[][]) map.get("error");

  //遍歷並填充
  for (int i = 0; i < error.length; i++) {
   row++;
   sheet.addCell(new Label(0, row, error[i][0]));
   sheet.addCell(new Label(1, row, error[i][1]));
   sheet.addCell(new Label(2, row, error[i][2]));
   sheet.addCell(new Label(3, row, error[i][3],wcfFC));
  }
 }

    Excel檔案生成之後,由response傳給客戶,客戶選擇路徑,就可以下載了,至此,完成了Excel檔案下載、解析、和生成的工作,大功一件,希望有所啟發。

    另外,補充一下,火狐fireFox在上傳檔案時,伺服器端並不能獲取客戶端存取檔案的絕對路徑,而只是一個檔名,ie8以及低版本上傳檔案時,伺服器可以獲得絕對路徑,這是火狐的安全機制決定的,所以試圖根據上傳的檔案路徑,對檔案修改然後儲存到原檔案,這種是不可取的,一者是因為火狐下獲取不到檔案路徑,二者即使伺服器在ie下獲得了檔案的絕對路徑,在建立並修改檔案時也只是生成在伺服器端,並不能修改客戶端的檔案,只是提醒下,網上有解決方法,可以自行查閱。