Использование Sax parser на xml-файле внутри zip
Это может быть за пределами возможностей Java VM из-за размера обрабатываемых файлов (50-100 МБ xml-файлов)
Прямо сейчас у меня есть набор xml-файлов, отправленных в виде zips, которые, в свою очередь, все распаковываются, а затем все XML-файлы в каталоге обрабатываются по одному с помощью SAX.
Чтобы сэкономить время и пространство (так как сжатие составляет около 1:10), мне было интересно, есть ли способ передать ZipFileEntry, который является xml-файлом, обработчику SAX.
Я видел, как это делается с помощью DocumentBuilder и другие методы синтаксического анализа xml, но для peformance (и особенно памяти) я придерживаюсь SAX.
В настоящее время я использую SAX следующим образом
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser saxParser = factory.newSAXParser();
MyHandler handler = new MyHandler();
for( String curFile : xmlFiles )
{
System.out.println( "nnt>>>>> open " + curFile + " <<<<<n");
saxParser.parse( "file://" + new File( dirToProcess + curFile ).getAbsolutePath(), handler );
}
2 ответа:
Вы можете проанализировать XML, используя InputStream в качестве источника. Таким образом, вы можете открыть ZipFile, получить входной поток нужной записи, а затем проанализировать его. Смотрите методgetInputStream .
---- Edit - - - -
Вот некоторый код, который поможет вам:
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(); } }
ZipInputStream.read()
прочитал бы x количество байт изZipFileEntry
, распаковал бы их и дал вам распакованные байты.- Используйте любой из методов здесь для создания потока ввода/вывода.
- передайте этотвходящий/исходящий поток как
InputStream
вашему синтаксическому анализатору.- начните записывать распакованные данные в поток in/out (теперь обрабатывается как
OutputStream
).- Итак, теперь Вы читаете куски данных из zip-файла, распаковываете их и передаете синтаксическому анализатору.
PS:
- Если zip-файл содержит несколько файлов смотрите это: извлекая содержимое записей ZipFile при чтении из byte [] (Java) , вы должны будете поставить такую проверку, чтобы вы знали, когда вы достигнете конца записи.
- я не очень разбираюсь в Sax parser, но предполагаю, что он будет разбирать файл таким образом (когда дается кусками).
- - - edit - - -
Вот что я имел в виду:
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(); } } }