原来做文件上传的时候,都是有一个输入框,点击上传按钮,先浏览文件,选择文件后,把文件的路径保存到form表单中,最后通过form表单提交到服务端。这样的界面不是很美观。为了用户有更好地体验(UE),现在的大多数系统都是采用一键文件上传,用户点击上传按钮,选择要上传的文件,确定之后,文件就直接上传了,不需要提供额外的form表单,而且可以实现页面文件上传无刷新。

<>文件上传和一键文件上传的原理

文件上传需要具备的条件:

客户端:
1、 Form 表单编码方式 multipart/form-data
2、 提交方式必须为 post
3、 上传文件对应 input type=”file” 元素要提供 name 属性

服务端:

使用Struts2进行文件上传

struts2-default.xml 配置



FileUploadInterceptor拦截器已经被配置 defaultStack 中,这个拦截器对Apache commons-fileupload
文件上传工具包进行了封装,使用更便捷。

<>客户端–使用jQuery ocupload插件实现一键上传

Ocupload即One-click upload,一键上传。

一键上传原理



插件下载的官网现在不能正常访问,插件在文章末提供的示例项目中。示例项目仅供大家参考!

首先在项目中导入这个插件,并在页面中进行引用。
注:由于这个插件是jQuery的插件,所在需要在引入这个插件之前,先在页面中引入jQuery文件。
插件的使用

官方示例:
$(element).upload({ name: 'file', //<input type='file' name='file'/>
插件的默认值是file action: '', //请求服务器的路径 enctype: 'multipart/form-data', //mime类型
,插件默认的即可 params: {}, //请求额外传递的参数,一般不用设置 autoSubmit: true, //是否在选择文件后自动提交form表单
onSubmit: function() {}, //在提交form表单之前进行的操作 onComplete: function() {},
//完成文件上传后,进行的操作 onSelect: function() {} //选择文件后,进行的操作 });
代码演示:
//为按钮绑定一键上传插件 $("#ocupload").upload({ action:
'../ocupload_batchImport.action', //请求服务端的路径
//完成上传后,触发的事件,response参数是响应到页面的json字符串 onComplete: function(response) {
//将返回的json字符串response转换成json对象 var data = JSON.parse(response);
if(data.result){ alert("文件上传成功!"); }else{ alert("文件上传失败!"); } }, //选择文件后,触发的事件
onSelect: function() { this.autoSubmit = false; //这个是一键上传插件中的属性
//定义一个正则表达式,用来限定只能上传以.xls或以.xlsx结尾的Excel文件 var regex = /^.*\.(xls|xlsx)$/ ;
//获得上传文件的名字 var filename = this.filename(); //这个方法是由一键上传的插件提供
if(regex.test(filename)){ //符合条件,提交form表单 this.submit(); //这个方法是由一键上传的插件提供
}else{ //不符合条件,给出提示 alert("只能上传以.xls或以.xlsx结尾的文件!"); } }
<>服务端–使用Apache POI解析Excel数据

编写 OcuploadAction类 接收上传文件

由于Struts2的FileUploadInterceptor拦截器对文件上传工具包进行了封装,我们只需要按照这个拦截器中定义的规范,即可完成文件的上传。

在 Action 定义三个成员变量
private File [页面元素 name]
private String [页面元素 name]ContentType;
private String [页面元素 name]FileName;

并在Action中提供set方法

Apache POI

POI的功能可以解析微软的Office组件的文档格式。
企业中通常使用其解析Excel文档或生成Excel文档(SS)。
POI支持HSSF解析(.xls–Excel97-2007之前版本,仅支持65535行记录)和XSSF解析(.xlsx–Excel2007及以后版本)。

1,在项目中引入POI的坐标(根据需要可以引入HSSF和XSSF的)
<poi.version>3.12</poi.version> <!-- 解析HSSF的包 就是解析Excel
97-2007格式的Excel文档(以.xls结尾的)--> <dependency> <groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId> <version>${poi.version}</version> </dependency>
<!-- 解析XSSF的包 解析Excel 2007格式的Excel文档(以.xlsx结尾的) 这个包依赖poi包--> <dependency>
<groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId>
<version>${poi.version}</version> </dependency> <dependency>
<groupId>org.apache.poi</groupId> <artifactId>poi-ooxml-schemas</artifactId>
<version>${poi.version}</version> </dependency>
2.解析Excel工作簿

POI解析的基本过程:打开WorkBook工作簿文件—》找到Sheet工作表—》遍历读取Rows行—》读取行中的cell单元格。
@ParentPackage("json-default") @Namespace("/") @Controller @Scope("prototype")
public class OcuploadAction extends BaseAction { // 属性驱动,获取客户端请求过来的file文件
private File file; // 属性名对应客户端form表单中设置的name属性的值 private String
fileContentType; // 上传文件的类型 private String fileFileName; // 上传文件的名称 public void
setFile(File file) { this.file = file; } public void setFileContentType(String
fileContentType) { this.fileContentType = fileContentType; } public void
setFileFileName(String fileFileName) { this.fileFileName = fileFileName; }
private String JSON = "json"; @Autowired private OcuploadService
ocuploadService; @Action(value = "ocupload_batchImport", results = {
@Result(name = "json", type = "json") }) public String batchImport() { //
定义一个存放返回结果集的map Map<String, Object> resultMap = new HashMap<>(); //
定义一个list集合,用户存放实体对象 List<Area> list = new ArrayList<>(); FileInputStream
inputStream = null; Workbook book = null; try { // Excel文件解析 //
1.创建文件输入流对象,读取文件 inputStream = new FileInputStream(file); //
2.创建Excel工作簿文件(包括.xls和.xlsx格式) book = WorkbookFactory.create(inputStream); //
3.打开需要进行解析的工作表sheet Sheet sheet = book.getSheetAt(0); //
4.遍历工作表对象sheet,获取到工作表中的每一行数据,对应一个实体对象(Area) for (Row row : sheet) { //
跳过第一行比表头数据 if (row.getRowNum() == 0) { continue; } //
一般来说,每一行的第一列都是标识列,如果第一列的单元格没有数据,则认为这一行数据无效,跳过 if
(StringUtils.isNotBlank(row.getCell(0).getStringCellValue())) { //
设置Area实体的部分属性 Area area = setEntity(row); //
============================================================ //
使用PinYin4j把字符串转成拼音 // 去掉省份,城市,区域最后一个字(省,市,区) // 省份 String province =
area.getProvince().substring(0, area.getProvince().length() - 1); // 城市 String
city = area.getCity().substring(0, area.getCity().length() - 1); // 区域 String
district = area.getDistrict().substring(0, area.getDistrict().length() - 1);
hanziTopinyin(area, province, city, district); // 把对象添加到list集合中 list.add(area);
} } // 5.调用业务层,批量导入数据 ocuploadService.batchImport(list); // 解析成功
resultMap.put("result", true); } catch (Exception e) {
System.out.println(e.getMessage()); // 解析失败 resultMap.put("result", false); }
finally { try { // 关闭资源 book.close(); inputStream.close(); } catch (Exception
e) { e.printStackTrace(); } } //
把map集合压入值栈,struts-json-plugin插件包,会把map自动转换成json数据 pushToValueStack(resultMap);
// 返回json数据 return JSON; } // 把汉字转换成拼音 public void hanziTopinyin(Area area,
String province, String city, String district) { // 得到区域简码 例如:北京市北京市海淀区
简码:BJBJHD String[] headStr = PinYin4jUtils.getHeadByString(province + city +
district); // 进行字符串拼接 StringBuffer buffer = new StringBuffer(); for (String str
: headStr) { buffer.append(str); } // 把buffer转换成string,得到区域简码 String shortcode
= buffer.toString(); // 设置区域简码 area.setShortcode(shortcode); // 设置城市编码
area.setCitycode(PinYin4jUtils.hanziToPinyin(city, "")); } /** * @param row *
@return */ public Area setEntity(Row row) { // 创建一个Area对象,把数据存放到这个对象中, Area
area = new Area(); // 设置数据时,要对应上传文件的Excel表格中的列进行设置 // 设置区域编号
area.setId(row.getCell(0).getStringCellValue()); // 设置省份
area.setProvince(row.getCell(1).getStringCellValue()); // 设置城市
area.setCity(row.getCell(2).getStringCellValue()); // 设置区域
area.setDistrict(row.getCell(3).getStringCellValue()); // 设置邮编
area.setPostcode(row.getCell(4).getStringCellValue()); return area; } }
业务操作,添加数据到数据库就不在这里叙述,完整源码在文章末的示例项目中!

在上述代码中,还有关于PinYin4j的用法,下面做简单的介绍

Pinyin4j是一个流行的Java库,支持中文字符和拼音之间的转换,拼音输出格式可以定制。

Pinyin4j的使用

在项目中导入Pinyin4j的坐标
<pinyin4j.version>2.5.0</pinyin4j.version> <!-- 支持字符串转换拼音 --> <dependency>
<groupId>pinyin4j</groupId> <artifactId>pinyin4j</artifactId>
<version>${pinyin4j.version}</version> </dependency>
在示例项目里,提供了一个Pinyin4jUtils工具类,它对Pinyin4j的一些操作进行了封装。

支持的方法:

1.获得每个汉字拼音首字母
2.把汉字转成拼音,去掉每个字拼音之间的空格

例如:北京市
获得每个汉字拼音首字母 [B, J, S]
把汉字转成拼音,去掉每个字拼音之间的空格 beijingshi
public static void main(String[] args) { // pin4j 简码 和 城市编码 String s1 = "北京市";
String[] headArray = getHeadByString(s1); // 获得每个汉字拼音首字母
System.out.println(Arrays.toString(headArray)); String s2 =
hanziToPinyin(s1,""); //把汉字转成拼音,去掉每个字拼音之间的空格 System.out.println(s2); }
最后分享示例项目在GitHub上的地址:https://github.com/xiaoguige/ocupload
<https://github.com/xiaoguige/ocupload>