这两天在搞一个东西,单据帮助内容的导入导出。具体说就是将单据的帮助信息(字段名称和帮助内容)编辑成Excel,然后上传。后端解析流,使用poi解析Excel内容并保存到数据库中。这就是导入;从数据库读取内容并装入poi里,写入输出流让前端浏览器下载,这就是导出。
就是这么简单的功能,但我却搞了两天,上传的时候request并不是类型,下载的时候已经写入response的输出流了,但浏览器就是没有弹出下载框。在网上看了个遍都不知道为什么。
后来终于知道原因了,是因为我用了Ajax向服务端发送请求。Ajax只能传递xml或json(好像有4种,网上的说法感觉都不靠谱,找时间看一看Ajax权威指南这一类的书籍,一次过彻底弄明白吧),而文件的上传下载是涉及到二进制数据的,所以Ajax做不到,只能用form请求。
上传大概是这么处理:
// 创建上传文件form var form = $("<form>"), //定义一个form表单 resFrame = $('<iframe name="res" style="display:none;"></iframe>'), fileInput; form.attr('id', uploadFormId); form.attr('name', uploadFormId); form.attr('enctype','multipart/form-data'); //在form表单中添加查询参数 form.attr('method','post'); form.attr('action',""); form.append(resFrame); form.attr('target','res'); //文件浏览选择控件 fileInput = $('<input>'); fileInput.attr('id','uploadFile'); fileInput.attr('type','file'); fileInput.attr('name', "file[]"); form.append(fileInput);
重点是设置好enctype属性和file类型的input元素。再弄一个隐藏的iframe作为form的target,好让提交form请求后页面不会刷新(伪Ajax)
public String importAction(HttpServletRequest request,HttpServletResponse response, ModelMap modelMap) throws WafException,WafBizException{ Context ctx = WafContext.getInstance().getContext(); MultipartHttpServletRequest mReq = (MultipartHttpServletRequest)request; MultipartFile mf = mReq.getFile("file[]"); // .............. }
这样子提交请求,服务端拿到的request就是MultipartHttpServletRequest,用它才可以拿到上传的文件流。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
private Workbook getWb(MultipartFile mf) throws BOSException { // MultipartHttpServletRequest Workbook wb = null; try{ String name = mf.getOriginalFilename(); InputStream inputStream = mf.getInputStream(); String postfix = name.substring(name.lastIndexOf(".") + 1); if("xls".equals(postfix)){ POIFSFileSystem fs = null; fs = openWorkbookFile(inputStream); wb = new HSSFWorkbook(fs); }else if("xlsx".equals(postfix)){ wb = new XSSFWorkbook(inputStream); }else{ throw new BOSException("引入的不是以xls或xlsx为后缀的文件"); } }catch(IOException e){ throw new BOSException(e); } return wb; }
获取Workbook,用它来处理Excel的方式网上搜一大把
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
createDownloadForm = function(){ // 创建下载文件隐藏form var form = $("<form>"), //定义一个form表单 resFrame = $('<iframe name="res" style="display:none;"></iframe>'); form.attr('id', downloadFormId); form.attr('style','display:none'); //在form表单中添加查询参数 form.attr('method','post'); form.attr('action',""); form.append(resFrame); form.attr('target','res'); downloadForm = form; return form; }
下载也使用form提交请求,使用Ajax无效,Ajax返回的responseText是一堆看不懂的乱码,当然浏览器不会自动下载文件。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
if(isDownload) form.submit(); else{ waf.block.show({ text: '正在导入,请稍后...' }) form.ajaxSubmit({ url: "?method="+action, type: "post", async: false, //dataType:"json", success: function(data){ waf.block.hide(); if("success" == data.result){ waf.msgBox.showInfo("导入成功"); }else{ waf.msgBox.showInfo(data.summany); } }, error: function(data){ waf.block.hide(); waf.msgBox.showInfo("导入失败"); } }); }
还有很奇怪的一点,如果是下载,一定要用调用form.submit提交,用下面的AjaxSubmit提交浏览器也不会自动下载文件。上传文件应该两种方式都可以,但AjaxSubmit可以添加返回处理函数。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
// 文件名称 String fileName = billTypeEnum.getAlias() + "-帮助内容.xlsx"; // 导出,让前台浏览器自动下载 String headerStr="attachment;filename=" + new String(fileName.getBytes("utf-8"),"ISO8859-1"); response.setContentType(FileUploadUtils.getContentType("xlsx")); response.setHeader("Content-disposition",headerStr); ServletOutputStream output = null; output = response.getOutputStream(); wb.write(output); output.flush(); output.close();
后端大概这么处理。fileName是下载时的默认文件名。FileUploadUtils.getContentType("xlsx")返回的是"application/x-excel"。
以上代码片段,前端使用jQuery,后端使用java servlet