需求
從前端傳來一個.docx文檔,后端解析該文檔里的表格,對其中的數據進行處理。
(我也不知道為什么非要解析word文檔里面的表格,而不是直接傳一個excel。)
實現
1.前端用的elementui-upload,把整個.docx文件傳給后端。
2.后端controller接收這個文檔,注意接收的格式是MultipartFile不是file,不然就報500了。
3.后端service解析這個文檔:
public Map < String, Object > dealWord(MultipartFile file) throws IOException {
// 3.1 解析整個文檔
XWPFDocument xwpf = new XWPFDocument(file.getInputStream());
// 3.2 從文檔中獲取表格的迭代器
Iterator < XWPFTable > it = xwpf.getTablesIterator();
// 3.3 遍歷所有的表格,挨個解析
while (it.hasNext()) {
// 3.3.1 獲取到當前的表格
XWPFTable table = it.next();
// 3.3.2 獲取到行數據
List < XWPFTableRow > rows = table.getRows();
// 3.3.3 遍歷每行,獲取每個單元格
for (int i = 1; i < rows.size(); i++) {
XWPFTableRow row = rows.get(i);
List<XWPFTableCell> cells = row.getTableCells();
// 3.3.4 獲取每個單元格里面的文字
for(int j = 0;j<cells.size();j++){
String text = cells.get(j).getText()
}
}
}
如果只需要獲取每個單元格內的文字內容,不考慮換行,那么到這里就結束了。
如果需要解析單元格內的換行符,請繼續往下看。
接下來介紹這里面的大坑。
坑
一開始我以為XWPFTable是不能解析到換行符的,后來覺得怎么可能,就具體看了下XWPFTable里面的數據的結構。
但是用wps和office讀寫過的.docx文檔,解析出來的段落結構是不一樣的!
不過很好做兼容。
這里順便介紹下解析出來的結構吧!
以如下表格為例(WPS讀寫版本):
這個表格解析出來的數據是類似於這樣的:
字段解釋:
這里的runs我也不知道怎么解釋...看了看官方文檔也不太明白,希望有所了解的朋友可以補充。
wps和word讀寫過的表格,就是在runs這里發生了差異。
圖中可以看到,里面的一個段落paragrah里面有一個run對象,就是單元格1.
三個段落,各自有一個run對象。
但是在office讀寫過的表格里,這里可能有多個run對象,也就是單元格1可能被拆成多個run對象。
可以對比着看一下:
這里的單元格1被拆解成了單元格和1兩個run對象。
探究office
閑着也是閑着,探究一下office里面run對象的數量的規則吧。
推測:以數據類型分割整個段落的文字,表格是字符串文字,1是數字。
現在我們要解析的offcie讀寫的表格:
解析出來的結果:
對比一下:
這就有點整不明白了吧....以我有限的知識水平,不知道為什么要把3和L拆開,但又把4L%合並成一個run對象了....
解決辦法
這個問題挺好解決的:
要獲取單元格內的第一排內容:
獲取到tableCell,獲取所有的paragrahs,獲取第一個paragrah,遍歷獲取這個paragrah里面所有的runs.
這樣就同時能適配wps和office啦。
上一段代碼:
public Map < String, Object > dealWord(MultipartFile file) throws IOException {
// 3.1 解析整個文檔
XWPFDocument xwpf = new XWPFDocument(file.getInputStream());
// 3.2 從文檔中獲取表格的迭代器
Iterator < XWPFTable > it = xwpf.getTablesIterator();
// 3.3 遍歷所有的表格,挨個解析
while (it.hasNext()) {
// 3.3.1 獲取到當前的表格
XWPFTable table = it.next();
// 3.3.2 獲取到行數據
List < XWPFTableRow > rows = table.getRows();
// 3.3.3 遍歷每行,獲取每個單元格
for (int i = 1; i < rows.size(); i++) {
XWPFTableRow row = rows.get(i);
List<XWPFTableCell> cells = row.getTableCells();
// 3.3.4 獲取每個單元格里面的文字
for(int j = 0;j<cells.size();j++){
String text = cells.get(j).getText()
}
// 3.3.5 獲取單元格里面的第一段內容
// 如果想要獲取所有段的內容 用ArrayList接收一下哈 這里不演示了
for(int k = 0;k<cells.getParagraphs().size();k++){
String text = "";
// 遍歷第一段里面所有的run 注意這里只get了0的段落
for(int p = 0;p<cells.getParagraphs().get(0).getRuns().size();p++){
text = text + cells.getParagraphs().get(0).getRuns().get(p);
}
System.out.prrintln("單元格第一排的內容是:"+text);
}
}
}
是很簡單的一個遍歷啦,很容易適配,就寫到這里了噢。