Apache POI, меняющий тип файла Mime. Это можно исправить? - PullRequest
3 голосов
/ 28 марта 2019

У меня проблемы с Apache POI и File Mime Type. Я использую шаблон файла (Microsoft Word DOCX) для изменения некоторых значений с помощью Apache Poi. Исходный файл имеет тип MIME «application / vnd.openxmlformats-officedocument.wordprocessingml.document» (в linux: file -i {имя_файла}), но я обработал файл с помощью POI и сохранил, затем снова получил «application / octet» -stream "и я хочу сохранить файл с оригинальным типом пантомимы.

Я открываю файл в HEX-редакторе, оба файла оригинальные и измененные, и оба имеют одинаковые «магические числа» (50 4B 03 04), но размер файла различен, даже если тексты одинаковы. Так можно это исправить? У кого-нибудь есть такая же проблема? Я проверяю его в LibreOffice, и похоже, что он работает с Apache POI.

Любая помощь, любая информация поможет.

Ответы [ 2 ]

1 голос
/ 29 марта 2019

Как вы уже указали в комментарии, то, как Apache POI реорганизует пакет Office Open XML ZIP, приводит к неправильной интерпретации типа содержимого некоторыми инструментами.Файл Office Open XML (*.docx, *.xlsx, *.pptx) является ZIP архивом, но в некоторой степени Microsoft Office упаковывает этот архив, должен быть особенным.Хотя я не нашел, что именно.

Пример:

Начните с Document.docx, имеющего некоторый простой контент, который был сохранен в Microsoft Word.

Для этого, file -i производит:

axel@arichter:~/Dokumente/JAVA/poi/poi-4.0.1$ file -i Document.docx 
Document.docx: application/vnd.openxmlformats-officedocument.wordprocessingml.document; charset=binary

Теперь запустите этот код:

import java.io.FileOutputStream;
import java.io.FileInputStream;

import org.apache.poi.xwpf.usermodel.XWPFDocument;

public class WordReadAndReWrite {

 public static void main(String[] args) throws Exception {

  String inFilePath = "Document.docx";
  String outFilePath = "NewDocument.docx";

  XWPFDocument doc = new XWPFDocument(new FileInputStream(inFilePath));

  doc.createParagraph().createRun().setText("new text inserted");

  FileOutputStream out = new FileOutputStream(outFilePath); 
  doc.write(out);
  out.close();
  doc.close();
 }

}

Для полученного NewDocument.docx, file -i создаст:

axel@arichter:~/Dokumente/JAVA/poi/poi-4.0.1$ file -i NewDocument.docx 
NewDocument.docx: application/octet-stream; charset=binary

Но если мы делаем то же самое, не используя Apache POI ZipPackage , а вместо этого используем FileSystem для получения XML из пакета Office Open XML ZIP с использованием следующего кода:

import java.nio.file.Files;
import java.nio.file.FileSystems;
import java.nio.file.FileSystem;
import java.nio.file.Paths;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;

import org.w3c.dom.Document;
import org.w3c.dom.Node;

import javax.xml.transform.TransformerFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.dom.DOMSource;

public class WordReadAndReWriteFileSystem {

 public static void main(String[] args) throws Exception {

  String inFilePath = "Document.docx";
  String outFilePath = "NewDocument.docx";

  FileSystem fileSystem = FileSystems.newFileSystem(Paths.get(inFilePath), null);
  Path wordDocumentXml = fileSystem.getPath("/word/document.xml");

  DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
  Document xmlDocument = documentBuilder.parse(Files.newInputStream(wordDocumentXml, StandardOpenOption.READ));

  Node p = xmlDocument.createElement("w:p");
  Node r = xmlDocument.createElement("w:r");
  p.appendChild(r);
  Node t = xmlDocument.createElement("w:t");
  r.appendChild(t);
  Node text = xmlDocument.createTextNode("new text inserted");
  t.appendChild(text);

  Node body = xmlDocument.getElementsByTagName("w:body").item(0);
  Node sectPr = xmlDocument.getElementsByTagName("w:sectPr").item(0);
  body.insertBefore(p, sectPr);

  TransformerFactory transformerFactory = TransformerFactory.newInstance();
  Transformer transformer = transformerFactory.newTransformer();
  DOMSource domSource = new DOMSource(xmlDocument);
  Path tmpDoc = Files.createTempFile("wordDocument", "tmp");
  tmpDoc.toFile().deleteOnExit();
  StreamResult streamResult = new StreamResult(Files.newOutputStream(tmpDoc, StandardOpenOption.WRITE));
  transformer.transform(domSource, streamResult);

  fileSystem.close();

  Path tmpZip = Files.createTempFile("zipDocument", "tmp");
  tmpZip.toFile().deleteOnExit();
  Path path = Files.copy(Paths.get(inFilePath), tmpZip, StandardCopyOption.REPLACE_EXISTING);
  fileSystem = FileSystems.newFileSystem(path, null);
  wordDocumentXml = fileSystem.getPath("/word/document.xml");

  Files.copy(tmpDoc, wordDocumentXml, StandardCopyOption.REPLACE_EXISTING);
  fileSystem.close();

  Files.copy(tmpZip, Paths.get(outFilePath), StandardCopyOption.REPLACE_EXISTING);

 }

}

Тогда для полученного NewDocument.docx, file -i выдает:

axel@arichter:~/Dokumente/JAVA/poi/poi-4.0.1$ file -i NewDocument.docx 
NewDocument.docx: application/vnd.openxmlformats-officedocument.wordprocessingml.document; charset=binary
0 голосов
/ 29 марта 2019

Этот код показывает правильный тип MIME-файла для всех файлов, которые я тестирую:

public static void main(String[] args) {
    String fileName = "model_libreoffice.docx";
//        String fileName = "model_poi.docx";
//        String fileName = "model_msoffice.docx";
//        String fileName = "model_repacked_bz2.docx";

    try {
        InputStream is = Main.class.getResourceAsStream("/" + fileName);
        Tika t = new Tika();
        String mime = t.detect(is, fileName);
        System.out.println("----> "  + mime);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

После длительной отладки и тестирования, я думаю, что это проблема с проверкой сторонних файлов.Этот простой код показывает мне правильный тип MIME для всех файлов, которые я пытаюсь изменить, измененные MicrosoftOffice, LibreOffice, Apache Poi, Unzip и Zipping (переименованные в DOCX) файлы содержимого DOCX ...

Итак, ядумаю, что эту проблему вообще можно пометить как «решенную».

...