在 zip 中的 xml 文件上使用 SAX 解析器
Posted
技术标签:
【中文标题】在 zip 中的 xml 文件上使用 SAX 解析器【英文标题】:Using SAX parser on xml file inside a zip 【发布时间】:2012-09-06 18:59:13 【问题描述】:由于要处理的文件的大小(50-100MB xml 文件),这可能超出了 Java VM 的能力
现在我有一组以 zip 格式发送的 xml 文件,这些文件依次全部解压缩,然后使用 SAX 一次处理一个目录中的所有 XML。
为了节省时间和空间(因为压缩大约是 1:10),我想知道是否有办法将 ZipFileEntry(一个 xml 文件)传递给 SAX 处理程序。
我已经看到它使用 DocumentBuilder 和其他 xml 解析方法完成,但为了性能(尤其是内存)我坚持使用 SAX。
目前我正在通过以下方式使用 SAX
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser saxParser = factory.newSAXParser();
MyHandler handler = new MyHandler();
for( String curFile : xmlFiles )
System.out.println( "\n\n\t>>>>> open " + curFile + " <<<<<\n");
saxParser.parse( "file://" + new File( dirToProcess + curFile ).getAbsolutePath(), handler );
【问题讨论】:
【参考方案1】:ZipInputStream.read()
会从 ZipFileEntry
中读取 x 个字节,解压缩它们并为您提供解压缩的字节。
使用任何方法here 来创建输入/输出流。
将该输入/输出流作为InputStream
提供给您的解析器。
开始将解压缩的数据写入输入/输出流(现在被视为OutputStream
)。
因此,您现在正在从 zip 文件中读取数据块,解压缩它们并将它们传递给解析器。
PS:
-
如果 zip 文件包含多个文件,请参见:extracting contents of ZipFile entries when read from byte[] (Java),您必须进行检查,以便知道何时到达条目的末尾。
我不太了解 SAX 解析器,但假设它会以这种方式解析文件(当以块的形式给出时)。
--- 编辑---
这就是我的意思:
import java.io.File;
import java.io.InputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
public class Main
static class MyRunnable implements Runnable
private InputStream xmlStream;
private SAXParser sParser;
public MyRunnable(SAXParser p, InputStream is)
sParser = p;
xmlStream = is;
public void run()
try
sParser.parse(xmlStream, new DefaultHandler()
public void startElement(String uri, String localName, String qName, Attributes attributes)
throws SAXException
System.out.println("\nStart Element :" + qName);
public void endElement(String uri, String localName, String qName) throws SAXException
System.out.println("\nEnd Element :" + qName);
);
System.out.println("Done parsing..");
catch (Exception e)
e.printStackTrace();
final static int BUF_SIZE = 5;
public static void main(String argv[])
try
SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
ZipFile zip = new ZipFile(new File("D:\\Workspaces\\Indigo\\Test\\performance.zip"));
Enumeration<? extends ZipEntry> entries = zip.entries();
while (entries.hasMoreElements())
// in stream for parser..
PipedInputStream xmlStream = new PipedInputStream();
// out stream attached to in stream above.. we would read from zip file and write to this..
// thus passing whatever we write to the parser..
PipedOutputStream out = new PipedOutputStream(xmlStream);
// Parser blocks in in stream, so put him on a different thread..
Thread parserThread = new Thread(new Main.MyRunnable(saxParser, xmlStream));
parserThread.start();
ZipEntry entry = entries.nextElement();
System.out.println("\nOpening zip entry: " + entry.getName());
InputStream unzippedStream = zip.getInputStream(entry);
byte buf[] = new byte[BUF_SIZE]; int bytesRead = 0;
while ((bytesRead = unzippedStream.read(buf)) > 0)
// write to err for different color in eclipse..
System.err.write(buf, 0, bytesRead);
out.write(buf, 0, bytesRead);
Thread.sleep(150); // theatrics...
out.flush();
// give parser a couple o seconds to catch up just in case there is some IO lag...
parserThread.join(2000);
unzippedStream.close(); out.close(); xmlStream.close();
catch (Exception e)
e.printStackTrace();
【讨论】:
查看我在上面添加的当前实现示例,不确定如何通过 sax 解析调用使用流【参考方案2】:您可以parse a XML 使用 InputStream 作为源。所以你可以打开一个ZipFile,得到你想要的entry的InputStream,然后解析它。请参阅getInputStream 方法。
---- 编辑----
这里有一些代码可以指导你:
for( String curFile : xmlFiles )
ZipFile zip = new ZipFile(new File( dirToProcess + curFile));
Enumeration<? extends ZipEntry> entries = zip.entries();
while (entries.hasMoreElements())
ZipEntry entry = entries.nextElement();
InputStream xmlStream = zip.getInputStream(entry);
saxParser.parse( xmlStream, handler );
xmlStream.close();
【讨论】:
查看我在上面添加的当前实现示例,不确定如何通过 sax 解析调用使用流 似乎可以正常工作 - 尽管需要 30 分钟才能运行 - 非常大的文件。以上是关于在 zip 中的 xml 文件上使用 SAX 解析器的主要内容,如果未能解决你的问题,请参考以下文章
如何在Ruby on Rails上使用SAX解析器来处理大型XML文件