Excel文件一键上传并解析完成数据批量导入数据库

原来做文件上传的时候,都是有一个输入框,点击上传按钮,先浏览文件,选择文件后,把文件的路径保存到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,一键上传。

一键上传原理

Excel文件一键上传并解析完成数据批量导入数据库_第1张图片

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

首先在项目中导入这个插件,并在页面中进行引用。

注:由于这个插件是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结尾的文件!");
        	}
        }

服务端–使用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的)

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

你可能感兴趣的:(文件上传)