1. 程式人生 > >Spring Mvc poi實現檔案上傳

Spring Mvc poi實現檔案上傳

    上傳檔案有很多種方法,這裡主要講通過poi元件(jar包))實現檔案上傳。專案依賴commons-io.jar和commons-fileupload(版本沒有太大要求,能實現功能即可),樓主用的是commons-fileupload-1.3.1.jarcommons-io-2.4.jar

    主pom.xml配置

<dependency>
                <groupId>commons-io</groupId>
                <artifactId>commons-io</artifactId>
                <version>${commons.io.version}</version>
</dependency>
<dependency>
                <groupId>commons-fileupload</groupId>
                <artifactId>commons-fileupload</artifactId>
                <version>${commons.fileupload.version}</version>
</dependency>

建立WorkBook物件引用poijar包的maven依賴

<dependency>
    <groupId>org.jeecg</groupId>
    <artifactId>easypoi-base</artifactId>
    <version>2.3.0.2</version>
</dependency>

    除了引入上述兩種jar包,還可以在spring配置檔案中新增id為multipartResolverbean,用於控制檔案上傳的大小,

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">  
        <property name="defaultEncoding" value="UTF-8" />  
        <property name="maxUploadSize" value="10240000" />  
        <!-- 設定在檔案上傳時允許寫到記憶體中的最大值,以位元組為單位計算,預設是10240 -->  
        <!-- 但是經實驗,上傳檔案大小若小於此引數,則不會生成臨時檔案,故改為2048 -->  
        <property name="maxInMemorySize" value="2048" />    
</bean>  

準備工作做好後,開始編寫前端程式碼。前端用form表單,enctype一定要為"multipart/form-data",否則無法實現檔案的上傳。

<form:form id="file_form"
		action="${ctx}/endUser/upload"
		enctype="multipart/form-data" method="post">
		<div class="modal-dialog">
			<div class="modal-content">
				<div class="modal-header">
					<button type="button" class="close" data-dismiss="modal"
						aria-hidden="true">×</button>
					<h4 class="modal-title" id="blankTitle">
						匯入Excel
						<!-- 提示 -->
					</h4>
				</div>
				<div class="modal-body" id="modal-body">
					<div class="row">
						<div class="form-horizontal">
							<div class="form-group ">
								<label class="col-lg-4 col-sm-4 control-label">檔案選擇:</label>
								<div class="col-lg-8 col-sm-8">

									<input type="file" name="file" id="file_input" />

								</div>
							</div>
						</div>
					</div>
					<div class="row">
						<div class="form-horizontal">
							<div class="form-group ">
								<label class="col-lg-4 col-sm-4 control-label">檔案上傳:</label>
								<div class="col-lg-8 col-sm-8">
									<input type="submit" value="上傳" id='upFile-btn'>
								</div>
							</div>
						</div>
					</div>
				</div>
			</div>
		</div>
</form:form>

form提交時通過jquery校驗檔案上傳的格式,樓主這裡實現上傳excel檔案,所以校驗excel格式

$("#file_form").submit(
			function() {
				//首先驗證檔案格式
				var fileName = $('#file_input').val();
				if (fileName === '') {
					alert('請選擇檔案');
					return false;
				}
				var fileType = (fileName.substring(fileName
						.lastIndexOf(".") + 1, fileName.length))
						.toLowerCase();
				if (fileType !== 'xls' && fileType !== 'xlsx') {
					alert('檔案格式不正確,excel檔案!');
					return false;
				}
                        }
});

僅校驗是不夠的,還需要用ajaxSubmit進行假提交,即不真正提交檔案到伺服器,而是隻在後臺獲取上傳的檔案

$("#file_form").submit(
    $("#file_form").ajaxSubmit({
					dataType : "json",
					clearForm: true, 
					success : function(data, textStatus) {
						if (data.returnCode == 1) {
							console.log('上傳檔案成功');
							alert("上傳檔案成功");
						} else {
							console.log('檔案格式錯誤');
							alert("檔案格式錯誤");
						}
						return false;
					}
				}); 
			return false;
    });

後臺通過form的action轉發的url獲取上傳的檔案,方法名和url同名,檔案則是以MultipartFile型別傳遞的

        /**
    	 * /enduser/upload
    	 * 匯入Excel
    	 * @param request
    	 * @throws Exception 
    	 */
    	@RequestMapping(value="/upload",method= {RequestMethod.POST})
    	@ResponseBody
    	public String upload(@RequestParam(value = "file") MultipartFile file,HttpServletRequest request) throws Exception  {
    		
    		//判空
    		if(file.isEmpty()) {
    			return null;
    		}
    		logger.debug("原始檔名稱:"+file.getName());
    		logger.debug("==========開始轉換Excel檔案==========");
    		//MultipartFile轉換為File
    		File f = multipartToFile(file);
    		try {
				endUserProvider.importExcel(f);
			} catch (Exception e) {
				logger.error("==========解析Excel檔案失敗==========",e);
			}
    		return "redirect:/endUser/list";
    	}

值得注意的是,上傳的檔案自動繫結到MultipartFile。若檔案為空,MultipartFile不為空,而是用MultipartFile.isEmpty()判空。

檔案由MultipartFile轉為File後,由File得到檔案的輸入流is,通過WorkBookFactory建立WorkBook物件

MultipartFile轉為File

/** 
         * MultipartFile 轉換成File 
         *  
         * @param multfile 原檔案型別 
         * @return File 
         * @throws IOException 
         */  
        private File multipartToFile(MultipartFile multfile) throws IOException {  
            CommonsMultipartFile cf = (CommonsMultipartFile)multfile;   
            //這個myfile是MultipartFile的  
            DiskFileItem fi = (DiskFileItem) cf.getFileItem();  
            File file = fi.getStoreLocation();  
            //手動建立臨時檔案  
            if(file.length() < 0){  
                File tmpFile = new File(System.getProperty("java.io.tmpdir") + System.getProperty("file.separator") +   
                        file.getName());  
                multfile.transferTo(tmpFile);  
                return tmpFile;  
            }  
            return file;  
        }  
FileInputStream is = new FileInputStream(file);
workbook = WorkbookFactory.create(is);
is.close();

此處有更簡潔的思路,先將MultipartFile轉為File型別,然後獲取檔案輸入流來建立WorkBook物件

Workbook workbook = null;
try {
	InputStream is = file.getInputStream();
	workbook = WorkbookFactory.create(is);
	is.close();
    } catch (InvalidFormatException | IOException e) {
	.error("檔案流轉換為Workbook物件異常",e);
    }  

最後就是通過WorkBook物件得到Sheet物件,然後得到Excel的行和列,分別遍歷行列進行檔案的處理啦

for(int i=0;i<workbook.getNumberOfSheets();i++) {
    //獲取Excel檔案的第一個sheetSheet sheet = workbook.getSheetAt(i);  
    //獲取檔案的行
    int totalRows = sheet.getPhysicalNumberOfRows(); 
    //迴圈Excel行數  
    for (int r = 1; r < totalRows; r++) { 
        Row row = sheet.getRow(r);  
	if (row == null){  
	     continue;  
	            }
    //遍歷列  
        for (int c = 0; c < totalCells; c++) {  
	     Cell cell = row.getCell(c);
            //檔案處理
            }
      }
}

檔案下載

這裡說下大體思路,就不貼詳細程式碼了。

頁面上佈一個超連結,指向檔案下載的方法

檔案下載的方法

/**
         *  /endUser/excelDown
         * @param req
         * @param res
         * @throws IOException 
         */
        @RequestMapping(value="/excelDown")
        public void excelDown(HttpServletRequest req, HttpServletResponse res) throws IOException {
        	req.setCharacterEncoding("UTF-8");
        	res.setContentType("application/force-download");//應用程式強制下載
        	String path = req.getSession().getServletContext().getRealPath("/WEB-INF/ExcelModel/測試卡模板20180226.xls");
            File file = new File(path);
            // 取得檔名。
            String filename = file.getName();
            filename = URLEncoder.encode(filename, "UTF-8");
            // 以流的形式下載檔案。
            InputStream fis = new BufferedInputStream(new FileInputStream(path));
            byte[] buffer = new byte[fis.available()];
            fis.read(buffer);
            fis.close();
            // 清空response
            res.reset();
            // 設定response的Header
            res.addHeader("Content-Disposition", "attachment;filename=" + new String(filename.getBytes()));
            res.addHeader("Content-Length", "" + file.length());
            OutputStream toClient = new BufferedOutputStream(res.getOutputStream());
            res.setContentType("application/octet-stream");
            toClient.write(buffer);
            toClient.flush();
            toClient.close();
}

需要注意的地方

excel單元格為很長的數值型別時,可能獲取到科學計數法的數值。 所以需要格式化處理: DecimalFormat df = new DecimalFormat("0"); String value = df.format(cell.getNumericalValue());