原来做文件上传的时候,都是有一个输入框,点击上传按钮,先浏览文件,选择文件后,把文件的路径保存到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
文件上传工具包进行了封装,使用更便捷。
Ocupload即One-click upload,一键上传。
一键上传原理
插件下载的官网现在不能正常访问,插件在文章末提供的示例项目中。示例项目仅供大家参考!
首先在项目中导入这个插件,并在页面中进行引用。
注:由于这个插件是jQuery的插件,所在需要在引入这个插件之前,先在页面中引入jQuery文件。
插件的使用
官方示例:
$(element).upload({
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结尾的文件!");
}
}
编写 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的)
3.12
org.apache.poi
poi
${poi.version}
org.apache.poi
poi-ooxml
${poi.version}
org.apache.poi
poi-ooxml-schemas
${poi.version}
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 resultMap = new HashMap<>();
// 定义一个list集合,用户存放实体对象
List 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的坐标
2.5.0
pinyin4j
pinyin4j
${pinyin4j.version}
在示例项目里,提供了一个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