一、POI概述
Apache POI是Apache軟件基金會的開放源碼函式庫,POI提供API給Java程序?qū)icrosoft Office格式檔案讀和寫的功能。
結(jié)構(gòu):
HSSF - 提供讀寫Microsoft Excel格式檔案的功能。
XSSF - 提供讀寫Microsoft Excel OOXML格式檔案的功能。
HWPF - 提供讀寫Microsoft Word格式檔案的功能。
HSLF - 提供讀寫Microsoft PowerPoint格式檔案的功能。
HDGF - 提供讀寫Microsoft Visio格式檔案的功能。
使用必須引入依賴
org.apache.poi
poi
3.17
注:3.17版本是支持jdk6的最后版本
二、HSSF概況
HSSF 是Horrible SpreadSheet Format的縮寫,通過HSSF,你可以用純Java代碼來讀取、寫入、修改Excel文件。HSSF 為讀取操作提供了兩類API:usermodel和eventusermodel,即“用戶模型”和“事件-用戶模型”。
三、 POI EXCEL文檔結(jié)構(gòu)類
HSSFWorkbook excel文檔對象
HSSFSheet excel的sheet
HSSFRow excel的行
HSSFCell excel的單元格
HSSFFont excel字體
HSSFName 名稱
HSSFDataFormat 日期格式
HSSFHeader sheet頭
HSSFFooter sheet尾
HSSFCellStyle cell樣式
HSSFDateUtil 日期
HSSFPrintSetup 打印
HSSFErrorConstants 錯誤信息表
四、EXCEL的讀寫操作
1、讀取“區(qū)域數(shù)據(jù).xls”并儲存于list集合中,“區(qū)域數(shù)據(jù).xls”如下圖
public List
importXLS(){
ArrayList
list = new ArrayList<>();
try {
//1、獲取文件輸入流
InputStream inputStream = new FileInputStream("/Users/Shared/區(qū)域數(shù)據(jù).xls");
//2、獲取Excel工作簿對象
HSSFWorkbook workbook = new HSSFWorkbook(inputStream);
//3、得到Excel工作表對象
HSSFSheet sheetAt = workbook.getSheetAt(0);
//4、循環(huán)讀取表格數(shù)據(jù)
for (Row row : sheetAt) {
//首行(即表頭)不讀取
if (row.getRowNum() == 0) {
continue;
}
//讀取當前行中單元格數(shù)據(jù),索引從0開始
String areaNum = row.getCell(0).getStringCellValue();
String province = row.getCell(1).getStringCellValue();
String city = row.getCell(2).getStringCellValue();
String district = row.getCell(3).getStringCellValue();
String postcode = row.getCell(4).getStringCellValue();
Area area = new Area();
area.setCity(city);
area.setDistrict(district);
area.setProvince(province);
area.setPostCode(postcode);
list.add(area);
}
//5、關(guān)閉流
workbook.close();
} catch (IOException e) {
e.printStackTrace();
}
return list;
}
2、導(dǎo)出數(shù)據(jù)到“區(qū)域數(shù)據(jù).xls”文件中,頁面數(shù)據(jù)如下圖:
public void exportExcel() throws IOException {
Page
page = areaService.pageQuery(null);
List
list = page.getContent();
//1.在內(nèi)存中創(chuàng)建一個excel文件
HSSFWorkbook hssfWorkbook = new HSSFWorkbook();
//2.創(chuàng)建工作簿
HSSFSheet sheet = hssfWorkbook.createSheet();
//3.創(chuàng)建標題行
HSSFRow titlerRow = sheet.createRow(0);
titlerRow.createCell(0).setCellValue("省");
titlerRow.createCell(1).setCellValue("市");
titlerRow.createCell(2).setCellValue("區(qū)");
titlerRow.createCell(3).setCellValue("郵編");
titlerRow.createCell(4).setCellValue("簡碼");
titlerRow.createCell(5).setCellValue("城市編碼");
//4.遍歷數(shù)據(jù),創(chuàng)建數(shù)據(jù)行
for (Area area : list) {
//獲取最后一行的行號
int lastRowNum = sheet.getLastRowNum();
HSSFRow dataRow = sheet.createRow(lastRowNum + 1);
dataRow.createCell(0).setCellValue(area.getProvince());
dataRow.createCell(1).setCellValue(area.getCity());
dataRow.createCell(2).setCellValue(area.getDistrict());
dataRow.createCell(3).setCellValue(area.getPostcode());
dataRow.createCell(4).setCellValue(area.getShortcode());
dataRow.createCell(5).setCellValue(area.getCitycode());
}
//5.創(chuàng)建文件名
String fileName = "區(qū)域數(shù)據(jù)統(tǒng)計.xls";
//6.獲取輸出流對象
HttpServletResponse response = ServletActionContext.getResponse();
ServletOutputStream outputStream = response.getOutputStream();
//7.獲取mimeType
ServletContext servletContext = ServletActionContext.getServletContext();
String mimeType = servletContext.getMimeType(fileName);
//8.獲取瀏覽器信息,對文件名進行重新編碼
HttpServletRequest request = ServletActionContext.getRequest();
fileName = FileUtils.filenameEncoding(fileName, request);
//9.設(shè)置信息頭
response.setContentType(mimeType);
response.setHeader("Content-Disposition","attachment;filename="+fileName);
//10.寫出文件,關(guān)閉流
hssfWorkbook.write(outputStream);
hssfWorkbook.close();
}
工具類
public class FileUtils {
public static String filenameEncoding(String filename, HttpServletRequest request) throws IOException {
String agent = request.getHeader("User-Agent"); //獲取瀏覽器
if (agent.contains("Firefox")) {
BASE64Encoder base64Encoder = new BASE64Encoder();
filename = "=?utf-8?B?"
+ base64Encoder.encode(filename.getBytes("utf-8"))
+ "?=";
} else if(agent.contains("MSIE")) {
filename = URLEncoder.encode(filename, "utf-8");
} else if(agent.contains ("Safari")) {
filename = new String (filename.getBytes ("utf-8"),"ISO8859-1");
} else {
filename = URLEncoder.encode(filename, "utf-8");
}
return filename;
}
}
寫出xls文件:
五、 EXCEL常用操作方法
1、 得到Excel常用對象
POIFSFileSystem fs=newPOIFSFileSystem(new FileInputStream("d:/test.xls"));
//得到Excel工作簿對象
HSSFWorkbook wb = new HSSFWorkbook(fs);
//得到Excel工作表對象
HSSFSheet sheet = wb.getSheetAt(0);
//得到Excel工作表的行
HSSFRow row = sheet.getRow(i);
//得到Excel工作表指定行的單元格
HSSFCell cell = row.getCell((short) j);
cellStyle = cell.getCellStyle();//得到單元格樣式
2、建立Excel常用對象
HSSFWorkbook wb = new HSSFWorkbook();//創(chuàng)建Excel工作簿對象
HSSFSheet sheet = wb.createSheet("new sheet");//創(chuàng)建Excel工作表對象
HSSFRow row = sheet.createRow((short)0); //創(chuàng)建Excel工作表的行
cellStyle = wb.createCellStyle();//創(chuàng)建單元格樣式
row.createCell((short)0).setCellStyle(cellStyle); //創(chuàng)建Excel工作表指定行的單元格
row.createCell((short)0).setCellValue(1); //設(shè)置Excel工作表的值
3、設(shè)置sheet名稱和單元格內(nèi)容
wb.setSheetName(1, "第一張工作表",HSSFCell.ENCODING_UTF_16);
cell.setEncoding((short) 1);
cell.setCellValue("單元格內(nèi)容");
4、取得sheet的數(shù)目
wb.getNumberOfSheets()
5、 根據(jù)index取得sheet對象
HSSFSheet sheet = wb.getSheetAt(0);
6、取得有效的行數(shù)
int rowcount = sheet.getLastRowNum();
7、取得一行的有效單元格個數(shù)
row.getLastCellNum();
8、單元格值類型讀寫
cell.setCellType(HSSFCell.CELL_TYPE_STRING); //設(shè)置單元格為STRING類型
cell.getNumericCellValue();//讀取為數(shù)值類型的單元格內(nèi)容
9、設(shè)置列寬、行高
sheet.setColumnWidth((short)column,(short)width);
row.setHeight((short)height);
10、添加區(qū)域,合并單元格
Region region = new Region((short)rowFrom,(short)columnFrom,(short)rowTo
,(short)columnTo);//合并從第rowFrom行columnFrom列
sheet.addMergedRegion(region);// 到rowTo行columnTo的區(qū)域
//得到所有區(qū)域
sheet.getNumMergedRegions()
11、保存Excel文件
FileOutputStream fileOut = new FileOutputStream(path);
wb.write(fileOut);
12、根據(jù)單元格不同屬性返回字符串數(shù)值
public String getCellStringValue(HSSFCell cell) {
String cellValue = "";
switch (cell.getCellType()) {
case HSSFCell.CELL_TYPE_STRING://字符串類型
cellValue = cell.getStringCellValue();
if(cellValue.trim().equals("")||cellValue.trim().length()<=0)
cellValue=" ";
break;
case HSSFCell.CELL_TYPE_NUMERIC: //數(shù)值類型
cellValue = String.valueOf(cell.getNumericCellValue());
break;
case HSSFCell.CELL_TYPE_FORMULA: //公式
cell.setCellType(HSSFCell.CELL_TYPE_NUMERIC);
cellValue = String.valueOf(cell.getNumericCellValue());
break;
case HSSFCell.CELL_TYPE_BLANK:
cellValue=" ";
break;
case HSSFCell.CELL_TYPE_BOOLEAN:
break;
case HSSFCell.CELL_TYPE_ERROR:
break;
default:
break;
}
return cellValue;
}
13、常用單元格邊框格式
HSSFCellStyle style = wb.createCellStyle();
style.setBorderBottom(HSSFCellStyle.BORDER_DOTTED);//下邊框
style.setBorderLeft(HSSFCellStyle.BORDER_DOTTED);//左邊框
style.setBorderRight(HSSFCellStyle.BORDER_THIN);//右邊框
style.setBorderTop(HSSFCellStyle.BORDER_THIN);//上邊框
14、設(shè)置字體和內(nèi)容位置
HSSFFont f = wb.createFont();
f.setFontHeightInPoints((short) 11);//字號
f.setBoldweight(HSSFFont.BOLDWEIGHT_NORMAL);//加粗
style.setFont(f);
style.setAlignment(HSSFCellStyle.ALIGN_CENTER);//左右居中
style.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);//上下居中
style.setRotation(short rotation);//單元格內(nèi)容的旋轉(zhuǎn)的角度
HSSFDataFormat df = wb.createDataFormat();
style1.setDataFormat(df.getFormat("0.00%"));//設(shè)置單元格數(shù)據(jù)格式
cell.setCellFormula(string);//給單元格設(shè)公式
style.setRotation(short rotation);//單元格內(nèi)容的旋轉(zhuǎn)的角度
15、插入圖片
//先把讀進來的圖片放到一個ByteArrayOutputStream中,以便產(chǎn)生ByteArray
ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();
BufferedImage bufferImg = ImageIO.read(new File("ok.jpg"));
ImageIO.write(bufferImg,"jpg",byteArrayOut);
//讀進一個excel模版
FileInputStream fos = new FileInputStream(filePathName+"/stencil.xlt");
fs = new POIFSFileSystem(fos);
//創(chuàng)建一個工作薄
HSSFWorkbook wb = new HSSFWorkbook(fs);
HSSFSheet sheet = wb.getSheetAt(0);
HSSFPatriarch patriarch = sheet.createDrawingPatriarch();
HSSFClientAnchor anchor = new HSSFClientAnchor(0,0,1023,255,(short) 0,0,(short)10,10);
patriarch.createPicture(anchor , wb.addPicture(byteArrayOut.toByteArray(),HSSFWorkbook.PICTURE_TYPE_JPEG));
16、調(diào)整工作表位置
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet("format sheet");
HSSFPrintSetup ps = sheet.getPrintSetup();
sheet.setAutobreaks(true);
ps.setFitHeight((short)1);
ps.setFitWidth((short)1);
1、在學習從文件讀取數(shù)據(jù)中,寫了個示例代碼,讀取不在同一個目錄的file.txt,運行后報這個Python OSError: [Errno 22] Invalid argument:錯誤:
(1)、首先,在F盤的python_stu中新增了一個file.txt,同時在F盤的python_stu文件目錄底下新增一個file文件夾,里面有個file_reader.py來讀取python_stu文件目錄底下的file.txt,代碼分別如下:
file.txt:
測試
測試2
測試3
file_reader.py:
with open('F:\python_stu\file.txt') as file_obj:
contents = file_obj.read();
print(contents.rstrip());
(2)、運行后報錯:
(3)、出現(xiàn)這種錯誤的原因是由于讀取不到這個文件,看Traceback報的錯誤,最后一行,很明顯讀取不到file.txt,前面的F:\\python_stu沒錯,后面的名稱怎么變了,還是x0cile.txt。
(4)、解決辦法,可修改上述第一行代碼為:
with open('F:\python_stu/file.txt') as file_obj:
或者:
with open('F:/python_stu/file.txt') as file_obj:
或者:
with open('F://python_stu//file.txt') as file_obj:
又或者:
with open('F:\\python_stu\\file.txt') as file_obj:
還有一些我就不附上了,上面第一種方式不統(tǒng)一,最好不要用,用統(tǒng)一的方式,而且有時候還有注意一些轉(zhuǎn)義字符,比如 \t,\n也會導(dǎo)致報錯。
前面學習了使用命令hdfs haadmin -failover手動進行故障轉(zhuǎn)移,在該模式下,即使現(xiàn)役NameNode已經(jīng)失效,系統(tǒng)也不會自動從現(xiàn)役NameNode轉(zhuǎn)移到待機NameNode,下面學習如何配置部署HA自動進行故障轉(zhuǎn)移。自動故障轉(zhuǎn)移為HDFS部署增加了兩個新組件:ZooKeeper和ZKFailoverController(ZKFC)進程。ZooKeeper是維護少量協(xié)調(diào)數(shù)據(jù),通知客戶端這些數(shù)據(jù)的改變和監(jiān)視客戶端故障的高可用服務(wù)。HA的自動故障轉(zhuǎn)移依賴于ZooKeeper的以下功能:
- 故障檢測:集群中的每個NameNode在ZooKeeper中維護了一個持久會話,如果機器崩潰,ZooKeeper中的會話將終止,ZooKeeper通知另一個NameNode需要觸發(fā)故障轉(zhuǎn)移。
- 現(xiàn)役NameNode選擇:ZooKeeper提供了一個簡單的機制用于唯一的選擇一個節(jié)點為active狀態(tài)。如果目前現(xiàn)役NameNode崩潰,另一個節(jié)點可能從ZooKeeper獲得特殊的排外鎖以表明它應(yīng)該成為現(xiàn)役NameNode。
ZKFC是自動故障轉(zhuǎn)移中的另一個新組件,是ZooKeeper的客戶端,也監(jiān)視和管理NameNode的狀態(tài)。每個運行NameNode的主機也運行了一個ZKFC進程,ZKFC負責:
- 健康監(jiān)測:ZKFC使用一個健康檢查命令定期地ping與之在相同主機的NameNode,只要該NameNode及時地回復(fù)健康狀態(tài),ZKFC認為該節(jié)點是健康的。如果該節(jié)點崩潰,凍結(jié)或進入不健康狀態(tài),健康監(jiān)測器標識該節(jié)點為非健康的。
- ZooKeeper會話管理:當本地NameNode是健康的,ZKFC保持一個在ZooKeeper中打開的會話。如果本地NameNode處于active狀態(tài),ZKFC也保持一個特殊的znode鎖,該鎖使用了ZooKeeper對短暫節(jié)點的支持,如果會話終止,鎖節(jié)點將自動刪除。
- 基于ZooKeeper的選擇:如果本地NameNode是健康的,且ZKFC發(fā)現(xiàn)沒有其它的節(jié)點當前持有znode鎖,它將為自己獲取該鎖。如果成功,則它已經(jīng)贏得了選擇,并負責運行故障轉(zhuǎn)移進程以使它的本地NameNode為active。故障轉(zhuǎn)移進城與前面描述的手動故障轉(zhuǎn)移相似,首先如果必要保護之前的現(xiàn)役NameNode,然后本地NameNode轉(zhuǎn)換為active狀態(tài)。
在典型部署中,ZooKeeper守護進程運行在三個或者五個節(jié)點上,但由于ZooKeeper本身需要較少的資源,所以將ZooKeeper部署在與現(xiàn)役NameNode和待機NameNode相同的主機上,還可以將ZooKeeper部署到與YARN的ResourceManager相同的節(jié)點上。建議配置ZooKeeper將數(shù)據(jù)存儲在與HDFS元數(shù)據(jù)不同的硬盤上以得到最好的性能和隔離性。在配置自動故障轉(zhuǎn)移之前需要先停掉集群,目前在集群運行時還不可能將手動故障轉(zhuǎn)移的安裝轉(zhuǎn)換為自動故障轉(zhuǎn)移的安裝。接下來看看如何配置HA的自動故障轉(zhuǎn)移。首先在hdfs-site.xml中添加下面的參數(shù),該參數(shù)的值默認為false:
- <property>
- <name>dfs.ha.automatic-failover.enabled</name>
- <value>true</value>
- </property>
在core-site.xml文件中添加下面的參數(shù),該參數(shù)的值為ZooKeeper服務(wù)器的地址,ZKFC將使用該地址。
- <property>
- <name>ha.zookeeper.quorum</name> <value>zk1.example.com:2181,zk2.example.com:2181,zk3.example.com:2181</value>
- </property>
在HA或者HDFS聯(lián)盟中,上面的兩個參數(shù)還需要以NameServiceID為后綴,比如dfs.ha.automatic-failover.enabled.mycluster。除了上面的兩個參數(shù)外,還有其它幾個參數(shù)用于自動故障轉(zhuǎn)移,比如ha.zookeeper.session-timeout.ms,但對于大多數(shù)安裝來說都不是必須的。
在添加了上述的配置參數(shù)后,下一步就是在ZooKeeper中初始化要求的狀態(tài),可以在任一NameNode中運行下面的命令實現(xiàn)該目的,該命令將在ZooKeeper中創(chuàng)建znode:
在啟用自動故障轉(zhuǎn)移的集群中,start-dfs.sh腳本將在任何運行NameNode的主機上自動啟動ZKFC守護進程,一旦ZKFC啟動完畢,它們將自動選擇一個NameNode為現(xiàn)役NameNode。如果手動管理集群中的服務(wù),需要在每臺運行NameNode的主機上手動啟動ZKFC,命令為:
- hadoop-daemon.sh start zkfc
- hdfs zkfc
如果正在運行一個安全的集群,可能想確保存儲在ZooKeeper中的信息也是安全的,這將阻止惡意的客戶端修改ZooKeeper中的元數(shù)據(jù)或者潛在地觸發(fā)一個錯誤的故障轉(zhuǎn)移。為了保護ZooKeeper中的信息,首先在core-site.xml中添加下面的參數(shù):
- <property>
- <name>ha.zookeeper.auth</name>
- <value>@/path/to/zk-auth.txt</value>
- </property>
- <property>
- <name>ha.zookeeper.acl</name>
- <value>@/path/to/zk-acl.txt</value>
- </property>
參數(shù)值中的@字符表示參數(shù)值保存在@后的硬盤文件中。第一個配置文件指定了ZooKeeper的認證列表,其格式與ZK CLI使用的相同,例如:digest:hdfs-zkfcs:mypassword,其中hdfs-zkfcs為ZooKeeper的用戶名,mypassword為密碼。其次使用下面的命令為該認證生成一個ZooKeeper訪問控制列表:
- $ java -cp $ZK_HOME/lib/*:$ZK_HOME/zookeeper-3.4.2.jar org.apache.zookeeper.server.auth.DigestAuthenticationProvider hdfs-zkfcs:mypassword
- output: hdfs-zkfcs:mypassword->hdfs-zkfcs:P/OQvnYyU/nF/mGYvB/xurX8dYs=
拷貝->之后的字符串并添加digest:前綴,然后粘貼到zk-acls.txt中,例如:digest:hdfs-zkfcs:vlUvLnd8MlacsE80rDuu6ONESbM=:rwcda。要想使ACLs生效,需要再次運行zkfc –formatZK。最后可能像下面這樣在ZK CLI中驗證ACLs:
- [zk: localhost:2181(CONNECTED) 1] getAcl /hadoop-ha
- 'digest,'hdfs-zkfcs:vlUvLnd8MlacsE80rDuu6ONESbM=
- : cdrwa
在安裝完成自動故障轉(zhuǎn)移后,或許需要測試一下。首先定位現(xiàn)役NameNode,可以通過訪問NameNode的web頁面來確定哪個NameNode是active狀態(tài)的。一旦確定了處于active狀態(tài)的NameNode,就需要在該節(jié)點上制造點故障,比如使用命令kill -9 <pid of NN>模擬JVM崩潰,或重啟主機或拔掉網(wǎng)線來模擬不同的中斷。一旦觸發(fā)了自動故障轉(zhuǎn)移,另一個NameNode應(yīng)該自動在幾秒鐘內(nèi)變?yōu)閍ctive狀態(tài)。檢測到故障并觸發(fā)故障轉(zhuǎn)移由參數(shù)ha.zookeeper.session-timeout.ms控制,該參數(shù)為與core-site.xml中,默認為5秒。如果測試不成功,可能是配置問題,檢查ZKFC和NameNode進程的日志以進一步診斷問題,通常錯誤都是很明顯的。