附件上传:
附件上传需要一个页面-fileUpload.jsp或者fileUpload.html,在页面中的input 必须有 name <input type="file" name="filename">,且这个表单的enctype属性就必须设置为multipart/form-data,因为浏览器表单的类型如果为multipart/form-data,那么浏览器在提交表单数据时,它将采用MIME协议对数据进行封装后提交,在服务器这端也将不能采用原来传统的方工获取数据了。在服务器端想获取数据就要通过流。request提供了一getInputStream读取流。可在java后台建立一个servlet负责处理上传的文件数据,并将表单中每个输入项封装成一个FileItem对象中。servlet可以解析请求时需要根据DiskFileItemFactory对象的属性sizeThreshold(临界值)和repository(临时目录)来决定将解析得到的数据保存在内存还是临时文件中,如果是临时文件,保存在哪个临时目录中。
其中,FileItem是一个接口,文件上传时需要用到,它的实现类是DiskFileItem。
FileItem类的常用方法:
1. boolean isFormField()
isFormField方法用于判断FileItem类对象封装的数据是一个普通文本表单字段,还是一个文件表单字段,如果是普通表单字段则返回true,否则返回false。因此,可以使用该方法判断是否为普通表单域,还是文件上传表单域。
2. String getName()
getName方法用于获得文件上传字段中的文件名。
注意IE或FireFox中获取的文件名是不一样的,IE中是绝对路径,FireFox中只是文件名。
3. String getFieldName()
getFieldName方法用于返回表单标签name属性的值。如上例中<input type="text" name="column" />的value。
4. void write(File file)
write方法用于将FileItem对象中保存的主体内容保存到某个指定的文件中。如果FileItem对象中的主体内容是保存在某个临时文件中,该方法顺利完成后,临时文件有可能会被清除。该方法也可将普通表单字段内容写入到一个文件中,但它主要用途是将上传的文件内容保存在本地文件系统中。
5. String getString()
getString方法用于将FileItem对象中保存的数据流内容以一个字符串返回,它有两个重载的定义形式:
public Java.lang.String getString()
public java.lang.String getString(java.lang.String encoding)
throws java.io.UnsupportedEncodingException
前者使用缺省的字符集编码将主体内容转换成字符串,后者使用参数指定的字符集编码将主体内容转换成字符串。如果在读取普通表单字段元素的内容时出现了中文乱码现象,请调用第二个getString方法,并为之传递正确的字符集编码名称。
6. String getContentType()
getContentType 方法用于获得上传文件的类型,即表单字段元素描述头属性“Content-Type”的值,如“image/jpeg”。如果FileItem类对象对应的是普通表单字段,该方法将返回null。
7. boolean isInMemory()
isInMemory方法用来判断FileItem对象封装的数据内容是存储在内存中,还是存储在临时文件中,如果存储在内存中则返回true,否则返回false。
8. void delete()
delete方法用来清空FileItem类对象中存放的主体内容,如果主体内容被保存在临时文件中,delete方法将删除该临时文件。
尽管当FileItem对象被垃圾收集器收集时会自动清除临时文件,但及时调用delete方法可以更早的清除临时文件,释放系统存储资源。另外,当系统出现异常时,仍有可能造成有的临时文件被永久保存在了硬盘中。
9. InputStream getInputStream()
以流的形式返回上传文件的数据内容。
10. long getSize()
返回该上传文件的大小(以字节为单位)。
Commons-fileupload和commons-io两个jar包.
为了方便用户处理文件上传数据,Apache开源组织提供了一个用来处理表单文件上传的一个开源组件(Commons-fileupload).使用Commons-fileupload组件实现文件上传,需要导入该组件相应的jar包.
Commons-fileupload和commons-io两个jar包.
DiskFileItemFactory是创建FileItem对象的工厂包括方法:
1.public void setSizeThreshold(int?sizeThreshold)
设置内存缓冲区的大小,默认值为10K,如果文件大于10K,将使用临时文件缓
存上传文件.
2.public void setRepository(Java.io.File repository)
指定临时文件目录
3.public DiskFileItemFactory();
ServletFileUpload类常用方法:
1) public void setSizeMax(long sizeMax)
setSizeMax方法继承自FileUploadBase类,用于设置请求消息实体内容(即所有上传数据)的最大尺寸限制,以防止客户端恶意上传超大文件来浪费服务器端的存储空间。其参数是以字节为单位的long型数字。
在请求解析的过程中,如果请求消息体内容的大小超过了setSizeMax方法的设置值,将会抛出FileUploadBase内部定义的SizeLimitExceededException异常(FileUploadException的子类)。该方法有一个对应的读方法:public long getSizeMax()方法。
2) public void setFileSizeMax(long fileSizeMax)
setFileSizeMax方法继承自FileUploadBase类,用于设置单个上传文件的最大尺寸限制,以防止客户端恶意上传超大文件来浪费服务器端的存储空间。其参数是以字节为单位的long型数字。该方法有一个对应的读方法:public long geFileSizeMax()方法。
在请求解析的过程中,如果单个上传文件的大小超过了setFileSizeMax方法的设置值,将会抛出FileUploadBase内部定义的FileSizeLimitExceededException异常(FileUploadException的子类)。
3) public List parseRequest(javax.servlet.http.HttpServletRequest req)
parseRequest 方法是ServletFileUpload类的重要方法,它是对HTTP请求消息体内容进行解析的入口方法。它解析出FORM表单中的每个字段的数据,并将它们分别包装成独立的FileItem对象,然后将这些FileItem对象加入进一个List类型的集合对象中返回。
该方法抛出FileUploadException异常来处理诸如文件尺寸过大、请求消息中的实体内容的类型不是“multipart/form-data”、IO异常、请求消息体长度信息丢失等各种异常。每一种异常都是FileUploadException的一个子类型。
4) public FileItemIterator getItemIterator(HttpServletRequest request)
getItemIterator方法和parseRequest 方法基本相同。但是getItemIterator方法返回的是一个迭代器,该迭代器中保存的不是FileItem对象,而是FileItemStream 对象,如果你希望进一步提高新能,你可以采用getItemIterator方法,直接获得每一个文件项的数据输入流,做底层处理;如果性能不是问题,你希望代码简单,则采用parseRequest方法即可。
5) public stiatc boolean isMultipartContent(HttpServletRequest req)
isMultipartContent方法方法用于判断请求消息中的内容是否是“multipart/form-data”类型,是则返回true,否则返回false。isMultipartContent方法是一个静态方法,不用创建ServletFileUpload类的实例对象即可被调用。
6) getFileItemFactory()和setFileItemFactory(FileItemFactory)
方法继承自FileUpload类,用于设置和读取fileItemFactory属性。
7) public void setProgressListener(ProgressListener pListener)
设置文件上传进度监听器。该方法有一个对应的读取方法:ProgressListener getProgressListener()。
8) public void setHeaderEncoding()
在文件上传请求的消息体中,除了普通表单域的值是文本内容以外,上传文件中的文件路径名也是文本(包括文件名),在内存中保存的是它们的某种字符集编码的字节数组,Apache文件上传组件在读取这些内容时,必须知道它们所采用的字符集编码,才能将它们转换成正确的字符文本返回。
setHeaderEncoding方法继承自FileUploadBase类,用于设置上面提到的字符编码。如果没有设置,则对应的读方法getHeaderEncoding()方法返回null,将采用HttpServletRequest设置的字符编码,如果HttpServletRequest的字符编码也为null,则采用系统默认字符编码。可以通过一下语句获得系统默认字符编码:
System.getProperty("file.encoding"));
DiskFileItemFactory类
将请求消息实体中的每一个项目封装成单独的DiskFileItem (FileItem接口的实现) 对象的任务
由 org.apache.commons.fileupload.FileItemFactory 接口的默认实现
org.apache.commons.fileupload.disk.DiskFileItemFactory 来完成。当上传的文件项目比较小时,直接保存在内存中(速度比较快),比较大时,以临时文件的形式,保存在磁盘临时文件夹(虽然速度慢些,但是内存资源是有限的)。
属性
1) public static final int DEFAULT_SIZE_THRESHOLD :将文件保存在内存还是磁盘临时文件夹的默认临界值,值为10240,即10kb。
2) private File repository:用于配置在创建文件项目时,当文件项目大于临界值时使用的临时文件夹,默认采用系统默认的临时文件路径,可以通过系统属性 java.io.tmpdir获取。如下代码:
System.getProperty("java.io.tmpdir");
3) private int sizeThreshold:用于保存将文件保存在内存还是磁盘临时文件夹的临界值
构造方法
1) public DiskFileItemFactory()
采用默认临界值和系统临时文件夹构造文件项工厂对象。
2) public DiskFileItemFactory(int sizeThreshold,File repository)
采用参数指定临界值和系统临时文件夹构造文件项工厂对象。
3) FileItem createItem()
根据DiskFileItemFactory相关配置将每一个请求消息实体项目创建成DiskFileItem 实例,并返回。该方法从来不需要我们亲自调用,FileUpload组件在解析请求时内部使用。
4) void setSizeThreshold(int sizeThreshold)
Apache文件上传组件在解析上传数据中的每个字段内容时,需要临时保存解析出的数据,以便在后面进行数据的进一步处理(保存在磁盘特定位置或插入数据库)。因为Java虚拟机默认可以使用的内存空间是有限的,超出限制时将会抛出“java.lang.OutOfMemoryError”错误。如果上传的文件很大,例如800M的文件,在内存中将无法临时保存该文件内容,Apache文件上传组件转而采用临时文件来保存这些数据;但如果上传的文件很小,例如600个字节的文件,显然将其直接保存在内存中性能会更加好些。
setSizeThreshold方法用于设置是否将上传文件已临时文件的形式保存在磁盘的临界值(以字节为单位的int值),如果从没有调用该方法设置此临界值,将会采用系统默认值10KB。对应的getSizeThreshold() 方法用来获取此临界值。
5) void setRepository(File repository)
setRepositoryPath方法用于设置当上传文件尺寸大于setSizeThreshold方法设置的临界值时,将文件以临时文件形式保存在磁盘上的存放目录。有一个对应的获得临时文件夹的 File getRespository() 方法。
注意:当从没有调用此方法设置临时文件存储目录时,默认采用系统默认的临时文件路径,可以通过系统属性 java.io.tmpdir 获取。如下代码:
System.getProperty("java.io.tmpdir");
Tomcat系统默认临时目录为“<tomcat安装目录>/temp/”。
代码示例-简单的实现excel的导入:
1.页面调用
$("#INPORT").on("click",function(){//导入按钮点击事件
$("#importExcel").show();//弹出导入的div
//请求相应的service,method,指定导入的excel所属模板,参数,以及回调函数
importExcel("service", "method", "excelTemplete", {}, function (responseStr) {
$("#importExcel").hide();//关闭弹窗
alert(responseStr);//打印上传结果信息
});
});
2.uploadExcel.jsp
<script>
function importExcel(serviceName, methodName, xlsType, parameter, okCallBack) {
$("#submitMyExcel").off("click");
$("#submitMyExcel").on("click", function () {
if (checkFile()) {
checkExcel(serviceName, methodName, xlsType, parameter, okCallBack);
IPLAT.progress($("body"), true); //使用提交动画,防止表单重复提交
}
})
}
function checkFile() {
var file_path = getFileName("myfile");
var fileName = file_path.replace(/(.*\/)*([^.]+).*/ig, "$2");
var FileExt = file_path.replace(/.+\./, "").toLowerCase();
if (!fileName) {
alert("文件不能为空");
return false;
}
else if (FileExt.indexOf("xls") < 0) {
alert("请选择excel文件");
return false;
}
// } else if(/.*[\u4e00-\u9fa5]+.*$/.test(fileName))
// {
// alert("文件名含有中文");
// }
return true;
}
function checkExcel(serviceName, methodName, xlsType_list, parameter, okCallBack) {
var file_path = getFileName("myfile");
var fileName = file_path.replace(/(.*\/)*([^.]+).*/ig, "$2");
var formData = new FormData();
var name = $("#myfile").val();
var file= $("#myfile")[0].files[0];
formData.append("file",file);
formData.append("name", fileName);
formData.append("serviceName", serviceName);
formData.append("methodName", methodName);
formData.append("parameter", parameter);
var xlsType = [];
if (xlsType_list.constructor === Array) {//存在name或sheet
for (var i = 0; i < xlsType_list.length; i++) {
var key_name = xlsType_list[i].hasOwnProperty("name");
var key_sheet = xlsType_list[i].hasOwnProperty("sheet");
var val_name = xlsType_list[i].name;
var val_type = xlsType_list[i].type;
//如果未指定sheet页,默认sheet1。否则按照指定sheet页对照
var val_sheet = "sheet1";
if (key_sheet) {
val_sheet = "sheet" + xlsType_list[i].sheet;
}
if (key_name)//存在名称,按名称导入
{
if (name.indexOf(val_name) >= 0) {
xlsType.push({"sheet":val_sheet,"type":val_type});
break;
}
}else{
xlsType.push({"sheet":val_sheet,"type":val_type});
}
}
}
else {
//未指定,则默认
xlsType.push({"sheet":"sheet1","type":xlsType_list});
}
if (xlsType == "") {
alert("参数错误");
return;
}
formData.append("xlsType", JSON.stringify(xlsType));
/* for (key in parameter) {
formData.append(key, parameter[key]);
}*/
$.ajax({
url: IPLATUI.CONTEXT_PATH + "/importExcel",
timeout: 300000,
type: 'POST',
async: true,
data: formData,
processData: false,
contentType: false,
beforeSend: function (res) {
IPLAT.progress($("body"), true);
},
success: function (res) {
IPLAT.progress($("body"), false);
if (typeof okCallBack == "function") {
okCallBack(res);
}
},
error: function (errorStr) {
IPLAT.progress($("body"), false);
}
});
}
function getFileName(id) {
var fileName = document.getElementById(id).value;
if (fileName != '') {
var index1 = fileName.lastIndexOf('/');
var index2 = fileName.lastIndexOf('\\');
var index = 0;
if (index1 != -1)
index = index1;
if (index2 != -1 && index2 > index1)
index = index2;
if (index == 0) {
alert("上传文件路径不正确");
return false;
} else
fileName = fileName.substring(index + 1);
return fileName;
} else
return false;
}
</script>
<div id="importExcel" title="文件上传" height="150px" width="550px" top="250px" left="500px">
<form action="" id="importMyExcel" enctype="multipart/form-data" method="post">
<input type="file" name="myfile" id="myfile"> </br>
<input type="button" value="导入" id="submitMyExcel" class="i-btn-lg" onclick=""/>
</form>
</div>
3.配置访问后台servlet的web.xml
<servlet>
<servlet-name>ImportExcel</servlet-name>
<servlet-class>com.pacage.项目名.common.excel.handler.ImportExcelServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ImportExcel</servlet-name>
<url-pattern>/importExcel</url-pattern>
</servlet-mapping>
4.后台对excle的简单处理方法ImportExcelServlet.java
package com.pacage.项目名.common.excel.handler;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.baosight.bxyw.common.excel.util.ImportExcelUtil;
import com.baosight.iplat4j.core.ei.EiConstant;
import com.baosight.iplat4j.core.ei.EiInfo;
import com.baosight.iplat4j.core.log.Logger;
import com.baosight.iplat4j.core.log.LoggerFactory;
import com.baosight.iplat4j.core.service.soa.XLocalManager;
import com.baosight.iplat4j.core.web.threadlocal.UserSession;
import net.sf.json.JSONArray;
/**
* EXCEL导入Servlet
* @author 18078
*
*/
public class ImportExcelServlet extends HttpServlet {
private static final long serialVersionUID = 2783045756722791019L;
Logger logger = LoggerFactory.getLogger(ImportExcelServlet.class);
public static final String SUCCESS = "1";
public static final String FAIL = "-1";
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
EiInfo outInfo =new EiInfo();
try {
//设置编码
response.setContentType("text/html;charset=UTF-8");
UserSession.web2Service(request);
//获取参数
Map params= paramManager.getMultiPartsParam(request);
params.put("userId",UserSession.getUserId());//用户编号
params.put("loginName",UserSession.getLoginName());//用户编号
//转数组
JSONArray ar_xlsType = JSONArray.fromObject(params.get("xlsType"));
HashMap xlsData= new LinkedHashMap();
xlsData=ImportExcelUtil.parseExcel((InputStream)params.get("inputStream"),ar_xlsType);
//存附件
//-----根据配置serviceName,methodName请求相应的excel处理方法start-----
//xlsData 即excel数据
//-----根据配置serviceName,methodName请求相应的excel处理方法end-----
} catch (Exception e) {
e.printStackTrace();
logger.error(e.getMessage());
outInfo.setMsg(e.toString());
outInfo.setStatus(-1);
response.getWriter().write(outInfo.toJSONString());
return;
}
response.getWriter().write(outInfo.toJSONString());
}
}
5.解析excel数帮助类ImportExcelUtil.java
package com.pacage.项目.common.excel.util;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.commons.lang.StringUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import com.baosight.bxyw.common.util.daoUtil.BeanFactoryUtils;
import com.baosight.bxyw.yn.bs.domain.MTXLS;
import com.baosight.iplat4j.core.data.ibatis.dao.Dao;
import com.baosight.iplat4j.core.ioc.spring.PlatApplicationContext;
import com.baosight.iplat4j.core.log.Logger;
import com.baosight.iplat4j.core.log.LoggerFactory;
import net.sf.json.JSONArray;
/**
* 导入office文件(xls)
*
* @author th
* @time 2018-02-23
*/
public class ImportExcelUtil {
private static final Dao dao = BeanFactoryUtils.getDaoLimitOf99999();
private static final Logger logger = LoggerFactory.getLogger(ImportExcelUtil.class);
private static final int MAX_COLS = 100;
private static final int MAX_ROWS = 10000;
//获取数据库配置表与excel文件的对照关系
public static Map queryMapping(String xlsType) {
HashMap map = new HashMap();
map.put("xlsType", "import");
if (!StringUtils.isBlank(xlsType)) {
map.put("xlsClassify", xlsType);
}
List<MTXLS> plist = dao.query("YNBS08.query", map);
Map<String, String> mapping = new HashMap<String, String>();
for (int i = 0; i < plist.size(); i++) {
MTXLS mtxls = plist.get(i);
String colcname = mtxls.getColCname();
String colename = mtxls.getColEname();
mapping.put(colcname.trim().replaceAll("\r|\n", ""), colename.trim().replaceAll("\r|\n", ""));
//mapping.put(colename, colcname);
}
return mapping;
}
/**
* 方法说明
*
* @param inStream 输入流
* @param mapping 字段名称映射
* @return 返回list
* @throws Exception
* @method 将流中的Excel数据转成List<Map>
* @author dada
* @date 2018-02-23
*/
public static HashMap parseExcel(InputStream inStream,
JSONArray xlsType) throws Exception {
//获取多个对照关系
Map<String,Map> mapping=new HashMap();
for(int i=0;i<xlsType.size();i++){
String map_sheet= xlsType.getJSONObject(i).getString("sheet");
String map_type=xlsType.getJSONObject(i).getString("type");
mapping.put(map_sheet, queryMapping(map_type));
}
// Map mapping = queryMapping(xlsType);
//excel操作类
Workbook work = WorkbookFactory.create(inStream);
Sheet sheet = null;
Row row = null;
Cell cell = null;
// 返回数据
HashMap SheetMap = new LinkedHashMap();
// 遍历Excel中所有的sheet
for (int i = 0; i < work.getNumberOfSheets(); i++) {
List emptyCol = new ArrayList();
List<Map<String, Object>> ls = new ArrayList<Map<String, Object>>();
sheet = work.getSheetAt(i);//遍历sheet
if (sheet == null) {
continue;
}
Map mapping_sheet=mapping.get("sheet"+(i+1));
// 取第一行标题
row = sheet.getRow(0);
String title[] = null;
if (row != null) {
//title = new String[mapping.size()];
title = new String[row.getLastCellNum()];//列头
for (int y = row.getFirstCellNum(); y < row.getLastCellNum(); y++) {
cell = row.getCell(y);
String key = (String)CellUtil.getCellValue(cell);
if ("".equals(key)) {
key = "col" + y;
emptyCol.add(key);
}
if (mapping_sheet.containsKey(key.trim().replaceAll("\r|\n", "")) || "none".equals(xlsType)) {
title[y] = key;
} else {
title[y] = "none";
}
}
} else//没有列头跳过
{
continue;
}
logger.info(JSONArray.fromObject(title));
// 遍历当前sheet中的所有行
for (int j = 1; j < sheet.getLastRowNum() + 1; j++) {
row = sheet.getRow(j);
if (row == null) {
continue;
}
Map<String, Object> map = new LinkedHashMap<String, Object>();
// 遍历所有的列
try {
for (int y = row.getFirstCellNum(); y < row.getLastCellNum(); y++) {
cell = row.getCell(y);
String key = title[y];
if (key == null) {
key = "col" + y;
}
boolean isMerge = CellUtil.isMergedRegion(sheet, j,y);
Object val=CellUtil.getCellValue(cell);
//读取合并单元格值
if(isMerge){
val=CellUtil.getMergedRegionValue(sheet, j, y);
}
if (!"none".equals(key)) {
if ("none".equals(xlsType)) {
map.put(key, val);
} else {
map.put(mapping_sheet.get(key).toString(), val);
//map.put(key, val);
}
}
}
} catch (Exception ex) {
String px = ex.toString();
}
if(map!=null)
{
ls.add(map);
}
}
filterList(ls, emptyCol);
SheetMap.put("sheet" + (i + 1), ls);
}
return SheetMap;
}
/*
* @Author: dada
* @Description: 去除解析过程中的无效列
* @Date: 19:21 2018/7/26/026
* @Param: [sheeMap]
* @return java.util.HashMap
**/
public static List<Map<String, Object>> filterList(List<Map<String, Object>> sheetList, List<String> emptyCol) {
List<String> removeCol = new ArrayList();
for (String col : emptyCol) {
List isEmptyList = sheetList
.stream()
.filter((Map e) -> "".equals(e.get(col)))
.map((Map e) -> e.get(col))
.collect(Collectors.toList());
if (isEmptyList.size() == sheetList.size()) {//都是空
removeCol.add(col);
}
}
for (Map<String, Object> row : sheetList) {
for (String col : removeCol) {
row.remove(col);
}
}
return sheetList;
}
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
String filename = "C:/Users/Administrator/Downloads/t.xlsx";
FileInputStream in = new FileInputStream(filename);
try {
Collection vals = null;//readXls(in);
System.out.println(vals);
System.out.println("import successfully." + (new Date()).toString());
} catch (Exception e) {
e.printStackTrace();
} finally {
in.close();
}
}
}
6.最终把excel数据导入数据
/**
* @return
*/
public EiInfo method(EiInfo info){
try{
HashMap xlsData =(HashMap)info.get("data");
List<Map<String, Object>> ls = (List<Map<String, Object>>)xlsData.get("sheet1");
int i = 0;
for (Map<String, Object> map : ls) {
i++;
//对map数据进行异常判断处理
//插入信息进入数据库
insert(map);
}
}catch(Exception e){
logger.error(e.getMessage());
return common.getReturn(msg, Constant.STATUS_FAILURE, "导入失败!"+e.getMessage());
}
return common.getReturn(msg, Constant.STATUS_SUCCESS, "导入成功!");
}