代碼地址見本文最後。
因爲特殊原因,更換了通訊工具,需要將原聊天消息進行備份,並能進行瀏覽或者查詢。
發現QQ消息可以導出mht格式的消息,這種文件格式內部其實就是講html、css、圖片(圖片是經過base64轉換)按照一定規律全部寫入到mht文件中的,只要按照規律解析即可。
在解析的過程中,如果是文件體積比較大,就需要考慮進行分頁,否則生成的html文件很大,我遇到解析後最大的單html文件達到了500M(導出全部消息),瀏覽起來很不方便,因此增加了分頁功能。
首先上效果:
1.將程序放到mht文件所在文件夾。
程序會自動查詢當前文件夾的mht文件,並進行轉換,最後將文件保存到mht同名的文件夾中。
2.雙擊運行run.bat文件,可選分頁。
注意:在mht文件小於100M的情況下,即使選擇了分頁,程序不會也不會進行分頁。
3.預覽效果
部分代碼解析:
1.生成單文件html
/**
* 創建單文件html
* @param inputFile
* @param outputFilePath
*/
public static void readAndCreateFile(String inputFile, String outputFilePath) {
String htmlFileName = parseHtmlFileName(inputFile);
File file = new File(inputFile);
BufferedInputStream fis = null;
BufferedReader reader = null;
try {
fis = new BufferedInputStream(new FileInputStream(file));
reader = new BufferedReader(new InputStreamReader(fis,"utf-8"),5*1024*1024);
boolean isCreatedHtml = false, isHtmlContent = false;
String line = "";
StringBuilder sb = null;
//String [] resType = null;
String resName = null;
//boolean isGetResType = false;
boolean isGetResName = false;
StringBuilder resSb = new StringBuilder();
while((line = reader.readLine()) != null){
if(!isCreatedHtml) {
if (isHtmlStartTag(line)) {
isHtmlContent = true;
sb = new StringBuilder(line).append("\n");
}else{
if(isHtmlContent) {
if (isHtmlEndTag(line)) {
sb.append(line).append("\n");
createHtmlFile(outputFilePath, htmlFileName, sb.toString(), 0, true);
sb.delete(0, sb.length());
isCreatedHtml = true;
} else {
sb.append(line).append("\n");
}
}
}
}
/**
* 開始解析資源文件
*/
if(isCreatedHtml) {
/*if(!isGetResType) {
resType = parseResourceType(line);
if (resType != null) {
isGetResType = true;
continue;
}
}*/
if(!isGetResName) {
resName = parseResourceName(line);
if (resName != null) {
isGetResName = true;
resSb.delete(0, resSb.length());
continue;
}
}
if(isGetResName) {
if(line.length() > 0) {
if(line.contains("------=_NextPart_")) {
//isGetResType = false;
isGetResName = false;
generateImage(resSb.toString(), (outputFilePath + File.separator + htmlFileName), resName);
}else{
resSb.append(line).append("\n");
}
}
}
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(reader != null) {
reader.close();
}
if(fis != null) {
fis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.生成分頁html
/**
* 創建分頁html
* @param inputFile
* @param outputFilePath
*/
public static void readAndCreateMultFile(String inputFile, String outputFilePath) {
String htmlFileName = parseHtmlFileName(inputFile);
File file = new File(inputFile);
BufferedInputStream fis = null;
BufferedReader reader = null;
try {
fis = new BufferedInputStream(new FileInputStream(file));
reader = new BufferedReader(new InputStreamReader(fis,"utf-8"),5*1024*1024);
boolean isCreatedHtml = false, isTableContent = false;
String line = "";
StringBuilder sb = null;
int trLine = 1;
int htmlNo = 1;
//String [] resType = null;
String resName = null;
//boolean isGetResType = false;
boolean isGetResName = false;
StringBuilder resSb = new StringBuilder();
while((line = reader.readLine()) != null){
if(!isCreatedHtml) {
Matcher startMatcher = tableStartPattern.matcher(line);
if (startMatcher.find()) {
isTableContent = true;
/**
* 將table後面的內容拼接起來
*/
sb = new StringBuilder(line.substring(startMatcher.end())).append("\n");
}else{
if(isTableContent) {
trLine++;
Matcher endMacher = tableEndPattern.matcher(line);
if (endMacher.find()) {
sb.append(line.substring(0, endMacher.start()));
createHtmlFile(outputFilePath, htmlFileName, sb.toString(), htmlNo, true);
isCreatedHtml = true;
} else {
sb.append(line).append("\n");
if(trLine % 1000 == 0) {
createHtmlFile(outputFilePath, htmlFileName, sb.toString(), htmlNo, false);
htmlNo ++;
sb.delete(0, sb.length());
}
}
}
}
}
/**
* 開始解析資源文件
*/
if(isCreatedHtml) {
/*if(!isGetResType) {
resType = parseResourceType(line);
if (resType != null) {
isGetResType = true;
continue;
}
}*/
if(!isGetResName) {
resName = parseResourceName(line);
if (resName != null) {
isGetResName = true;
resSb.delete(0, resSb.length());
continue;
}
}
if(isGetResName) {
if(line.length() > 0) {
if(line.contains("------=_NextPart_")) {
//isGetResType = false;
isGetResName = false;
generateImage(resSb.toString(), (outputFilePath + File.separator + htmlFileName), resName);
}else{
resSb.append(line).append("\n");
}
}
}
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(reader != null) {
reader.close();
}
if(fis != null) {
fis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
注意:
1.考慮到性能問題,分頁生成的時候,只做了上一頁、下一頁,因爲個人已經覺得這個夠用了,並沒有開發頁碼的功能,讀者可對這個進行擴展,思路大概是:將mht中的html部分讀取到,主要是table中的tr部分,然後將這些逐條讀取到並放入list,然後根據list進行分頁。
2.讀者在轉換之後,還是請保留您的原始mht文件,雖然功能經過測試,也對數據進行了一定量的驗證,但是不保證在轉換的過程中可能因bug或未知因素導致數據丟失,因此,請保留原始mht文件,請保留原始mht文件,請保留原始mht文件!
3.花了一點時間開發的,並未對代碼進行優化,可能有些地方有部分重複代碼,讀者請自行優化。
4.測試過6G和10G左右的文件,更大的文件暫時未測試。
後續:
後續如果有空閒時間,可能會做一定的改進,使用Lucene相關技術,對生成的html進行索引並支持搜索,方便搜索消息。
代碼地址:
https://github.com/itriders/mht2html