Spring Mvc poi實現檔案上傳
上傳檔案有很多種方法,這裡主要講通過poi元件(jar包))實現檔案上傳。專案依賴commons-io.jar和commons-fileupload(版本沒有太大要求,能實現功能即可),樓主用的是commons-fileupload-1.3.1.jar和commons-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為multipartResolver的bean,用於控制檔案上傳的大小,
<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());