#java #apache-poi #docx
#java #apache-poi #docx
Вопрос:
У меня проблемы с Apache POI и типом файла Mime. Я использую шаблон файла (Microsoft Word DOCX) для изменения некоторых значений с помощью Apache Poi. Исходный файл имеет mime-тип «application / vnd.openxmlformats-officedocument.wordprocessingml.document» (в Linux: file -i {filename}), но я обрабатываю файл с помощью POI и сохраняю, затем снова получаю «application / octet-stream» и хочу сохранить файл с исходным типом mime.
Я открываю файл с помощью шестнадцатеричного редактора, оба файла исходные и модифицированные, и оба имеют одинаковые «магические числа» (50 4B 03 04), но размер файла отличается, даже если тексты одинаковые. Так это возможно исправить? У кого-нибудь такая же проблема? Я проверяю это в LibreOffice и, похоже, имеет то же поведение, что и Apache POI.
Любая помощь, любая информация помогут.
Комментарии:
1. Удается ли Apache Tika правильно определять тип файла? Если да, то похоже на ошибку в вашей версии
file
инструмента2. Я анализирую документ и отправляю в другой сторонний API, и они не принимают docx с другим типом mime, должно быть «application / vnd.openxmlformats-officedocument.wordprocessingml.document». Но как можно манипулировать документом и сохранять этот тип mime? Я тоже пытаюсь в Windows, и у меня такая же проблема.
3. Тип mime файла OOXML задается
[Content_Types.xml]
файлом, встроенным в структуру zip. Apache POI устанавливает это правильно. Любое программное обеспечение, которое не проверяет, что делает это неправильно, и нуждается в указании на спецификацию Microsoft!4. Очень интересно! Я проверяю файлы, оба похожи, но не равны. Порядок, отступы элементов отличаются. Я действительно не уверен, почему stop работает в файловой системе Linux и Windows.
5. Я провел здесь тест. Мы распаковываем файл DOCX и снова переупаковываем в Windows с помощью 7zip, и тип mime в порядке (используя метод DEFLATE). Мы делаем тот же самый репак, используя 7zip, используя BZIP2, и тип mime, похоже, «application / zip». Итак, проблема заключается в методе, который Apache POI и LibreOffice используют для упаковки файла.
Ответ №1:
Как вы уже указывали в комментарии, то, как 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
Но если мы делаем то же самое, не используя ZipPackage Apache POI, а вместо этого используем файловую систему для получения 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.StreamResu<
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
Ответ №2:
Этот код показывает правильный тип файла 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, Распаковывает и снова архивирует (переименованный в DOCX) файлы содержимого DOCX…
Поэтому я думаю, что эту проблему вообще можно пометить как «решаемую».