Thẳng thắn mà nói, tôi đã không mong đợi một giải pháp trong thời gian tới và đã sắp bỏ cuộc, nhưng một số làm thế nào tôi vấp trên trang này:
http://en.wikipedia.org/wiki/MIME#Multipart_messages
http://msdn.microsoft.com/en-us/library/ms527355%28EXCHG.10%29.aspx
Mặc dù, không phải là một rất hấp dẫn trong cái nhìn đầu tiên. Nhưng nếu bạn nhìn kỹ, bạn sẽ có được đầu mối. Sau khi đọc xong, tôi đã kích hoạt trình duyệt IE của mình và bắt đầu lưu các trang như là *.mht
. Hãy để tôi đi từng dòng ...
Nhưng hãy để tôi giải thích trước rằng mục tiêu cuối cùng của tôi là tách/giải nén nội dung html
và phân tích cú pháp ... giải pháp không hoàn chỉnh vì nó phụ thuộc vào character set
hoặc encoding
Tôi chọn trong khi lưu. Nhưng ngay cả khi nó sẽ trích xuất các tập tin cá nhân với những trở ngại nhỏ ...
Tôi hy vọng điều này sẽ hữu ích cho bất cứ ai đang cố gắng để phân tích/giải nén file *.mht/MHTML
:)
Giải thích ======= ======== ** Taken từ một MHT tập tin **
From: "Saved by Windows Internet Explorer 7"
Đây là phần mềm sử dụng để lưu các tập tin
Subject: Google
Date: Tue, 13 Jul 2010 21:23:03 +0530
MIME-Version: 1.0
Chủ đề, ngày tháng và mime-phiên bản ... giống như những định dạng email
Content-Type: multipart/related;
type="text/html";
Đây là phần cho chúng tôi biết đây là tài liệu multipart
. Tài liệu nhiều phần có một hoặc nhiều tập hợp dữ liệu khác nhau được kết hợp trong một thân, một trường loại nội dung multipart
phải xuất hiện trong tiêu đề của thực thể. Ở đây, chúng ta cũng có thể thấy kiểu là "text/html"
.
boundary="----=_NextPart_000_0007_01CB22D1.93BBD1A0"
Trong số tất cả điều này là phần quan trọng nhất. Đây là dấu phân cách duy nhất phân chia hai phần khác nhau (html, hình ảnh, css, tập lệnh, v.v.). Sau khi bạn có được điều này, mọi thứ trở nên dễ dàng ... Bây giờ, tôi chỉ phải lặp qua tài liệu và tìm ra các phần khác nhau và lưu chúng theo Content-Transfer-Encoding
(base64, có thể in được, vv) ... . . .
MẪU
------=_NextPart_000_0007_01CB22D1.93BBD1A0
Content-Type: text/html;
charset="utf-8"
Content-Transfer-Encoding: quoted-printable
Content-Location: http://www.google.com/webhp?sourceid=navclient&ie=UTF-8
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" =
.
.
.
** JAVA MÃ **
Một giao diện để xác định hằng số.
public interface IConstants
{
public String BOUNDARY = "boundary";
public String CHAR_SET = "charset";
public String CONTENT_TYPE = "Content-Type";
public String CONTENT_TRANSFER_ENCODING = "Content-Transfer-Encoding";
public String CONTENT_LOCATION = "Content-Location";
public String UTF8_BOM = "=EF=BB=BF";
public String UTF16_BOM1 = "=FF=FE";
public String UTF16_BOM2 = "=FE=FF";
}
Lớp phân tích cú pháp chính ...
/**
* This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.test.mht.core;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.OutputStreamWriter;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import sun.misc.BASE64Decoder;
/**
* File to parse and decompose *.mts file in its constituting parts.
* @author Manish Shukla
*/
public class MHTParser implements IConstants
{
private File mhtFile;
private File outputFolder;
public MHTParser(File mhtFile, File outputFolder) {
this.mhtFile = mhtFile;
this.outputFolder = outputFolder;
}
/**
* @throws Exception
*/
public void decompress() throws Exception
{
BufferedReader reader = null;
String type = "";
String encoding = "";
String location = "";
String filename = "";
String charset = "utf-8";
StringBuilder buffer = null;
try
{
reader = new BufferedReader(new FileReader(mhtFile));
final String boundary = getBoundary(reader);
if(boundary == null)
throw new Exception("Failed to find document 'boundary'... Aborting");
String line = null;
int i = 1;
while((line = reader.readLine()) != null)
{
String temp = line.trim();
if(temp.contains(boundary))
{
if(buffer != null) {
writeBufferContentToFile(buffer,encoding,filename,charset);
buffer = null;
}
buffer = new StringBuilder();
}else if(temp.startsWith(CONTENT_TYPE)) {
type = getType(temp);
}else if(temp.startsWith(CHAR_SET)) {
charset = getCharSet(temp);
}else if(temp.startsWith(CONTENT_TRANSFER_ENCODING)) {
encoding = getEncoding(temp);
}else if(temp.startsWith(CONTENT_LOCATION)) {
location = temp.substring(temp.indexOf(":")+1).trim();
i++;
filename = getFileName(location,type);
}else {
if(buffer != null) {
buffer.append(line + "\n");
}
}
}
}finally
{
if(null != reader)
reader.close();
}
}
private String getCharSet(String temp)
{
String t = temp.split("=")[1].trim();
return t.substring(1, t.length()-1);
}
/**
* Save the file as per character set and encoding
*/
private void writeBufferContentToFile(StringBuilder buffer,String encoding, String filename, String charset)
throws Exception
{
if(!outputFolder.exists())
outputFolder.mkdirs();
byte[] content = null;
boolean text = true;
if(encoding.equalsIgnoreCase("base64")){
content = getBase64EncodedString(buffer);
text = false;
}else if(encoding.equalsIgnoreCase("quoted-printable")) {
content = getQuotedPrintableString(buffer);
}
else
content = buffer.toString().getBytes();
if(!text)
{
BufferedOutputStream bos = null;
try
{
bos = new BufferedOutputStream(new FileOutputStream(filename));
bos.write(content);
bos.flush();
}finally {
bos.close();
}
}else
{
BufferedWriter bw = null;
try
{
bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(filename), charset));
bw.write(new String(content));
bw.flush();
}finally {
bw.close();
}
}
}
/**
* When the save the *.mts file with 'utf-8' encoding then it appends '=EF=BB=BF'</br>
* @see http://en.wikipedia.org/wiki/Byte_order_mark
*/
private byte[] getQuotedPrintableString(StringBuilder buffer)
{
//Set<String> uniqueHex = new HashSet<String>();
//final Pattern p = Pattern.compile("(=\\p{XDigit}{2})*");
String temp = buffer.toString().replaceAll(UTF8_BOM, "").replaceAll("=\n", "");
//Matcher m = p.matcher(temp);
//while(m.find()) {
// uniqueHex.add(m.group());
//}
//System.out.println(uniqueHex);
//for (String hex : uniqueHex) {
//temp = temp.replaceAll(hex, getASCIIValue(hex.substring(1)));
//}
return temp.getBytes();
}
/*private String getASCIIValue(String hex) {
return ""+(char)Integer.parseInt(hex, 16);
}*/
/**
* Although system dependent..it works well
*/
private byte[] getBase64EncodedString(StringBuilder buffer) throws Exception {
return new BASE64Decoder().decodeBuffer(buffer.toString());
}
/**
* Tries to get a qualified file name. If the name is not apparent it tries to guess it from the URL.
* Otherwise it returns 'unknown.<type>'
*/
private String getFileName(String location, String type)
{
final Pattern p = Pattern.compile("(\\w|_|-)+\\.\\w+");
String ext = "";
String name = "";
if(type.toLowerCase().endsWith("jpeg"))
ext = "jpg";
else
ext = type.split("/")[1];
if(location.endsWith("/")) {
name = "main";
}else
{
name = location.substring(location.lastIndexOf("/") + 1);
Matcher m = p.matcher(name);
String fname = "";
while(m.find()) {
fname = m.group();
}
if(fname.trim().length() == 0)
name = "unknown";
else
return getUniqueName(fname.substring(0,fname.indexOf(".")), fname.substring(fname.indexOf(".") + 1, fname.length()));
}
return getUniqueName(name,ext);
}
/**
* Returns a qualified unique output file path for the parsed path.</br>
* In case the file already exist it appends a numarical value a continues
*/
private String getUniqueName(String name,String ext)
{
int i = 1;
File file = new File(outputFolder,name + "." + ext);
if(file.exists())
{
while(true)
{
file = new File(outputFolder, name + i + "." + ext);
if(!file.exists())
return file.getAbsolutePath();
i++;
}
}
return file.getAbsolutePath();
}
private String getType(String line) {
return splitUsingColonSpace(line);
}
private String getEncoding(String line){
return splitUsingColonSpace(line);
}
private String splitUsingColonSpace(String line) {
return line.split(":\\s*")[1].replaceAll(";", "");
}
/**
* Gives you the boundary string
*/
private String getBoundary(BufferedReader reader) throws Exception
{
String line = null;
while((line = reader.readLine()) != null)
{
line = line.trim();
if(line.startsWith(BOUNDARY)) {
return line.substring(line.indexOf("\"") + 1, line.lastIndexOf("\""));
}
}
return null;
}
}
Kính trọng,
Cảm ơn bạn đã chia sẻ điều này. Tôi có một câu hỏi mặc dù: Làm thế nào để bạn xác định kết thúc của một phần HEADER? Tôi có nghĩa là, phần bắt đầu với ranh giới được xác định ở đâu đó ở trên, sau đó có tiêu đề như kiểu/mã hóa/vị trí vv, và sau này dường như với tôi rằng mỗi tiêu đề được giới hạn bởi ASCII 0D0A0D0A (hai lần CR + LF). Tôi có đúng về điều này, hoặc có bất kỳ dấu phân cách nào khác được bảo đảm không? –
@favonius: Mã của bạn có chuyển đổi tệp .mht thành html không? –
@UmeshKumar: Mã được trích xuất dữ liệu. Nhưng tôi nghĩ rằng nó nên được đơn giản để chuyển đổi mht sang html. Google xung quanh có thể là một thư viện đã tồn tại. – Favonius