原來做文件上傳的時候,都是有一個輸入框,點擊上傳按鈕,先瀏覽文件,選擇文件後,把文件的路徑保存到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