Therefore the temp directory, of the user who is executing the jvm in which docx4j is used, is flooded if the method is used frequently.
- Used jar file: http://dev.plutext.org/docx4j/docx4j-nightly-20100630.jar
- Package containing the mentioned class: org.docx4j.openpackaging.parts.WordprocessingML
"Bug" - Enviroment:
- User: smokie
- OS Windows 7 x64
- Tempdir = C:\Users\smokie\AppData\Local\Temp
(Could also reproduce the bug under WIN XP / Just the temp dirs location differs)
- Code: Select all
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import org.docx4j.dml.wordprocessingDrawing.Inline;
import org.docx4j.jaxb.Context;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage;
import org.docx4j.utils.BufferUtil;
import org.docx4j.wml.Drawing;
import org.docx4j.wml.P;
import org.docx4j.wml.R;
public class Main {
/**
* Just a simple example which is reproducing an bug in the docx4j framework.
*/
public static void main(String[] args) throws Exception {
// Create the package
WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.createPackage();
// file object pointing to the picture which i wana use
File myImage = new File("C:\\Users\\smokie\\Pictures\\me.jpg");
// open an input stream to get the bytes of the picture
InputStream inputStream = new FileInputStream(myImage);
// get the bytes of the picture
byte[] imageBytes = BufferUtil.getBytesFromInputStream(inputStream);
// the next line of code will produce the "error"
// the method call will create a temp file in the users temp dir which is not removed!!
BinaryPartAbstractImage imagePart = BinaryPartAbstractImage.createImagePart(wordMLPackage, imageBytes);
// close the input stream
inputStream.close();
// put the picture into the document
Inline inlineImage = imagePart.createImageInline("filenameHint", "altText", 1, 2, false);
P p = Context.getWmlObjectFactory().createP();
R r = Context.getWmlObjectFactory().createR();
Drawing drawing = Context.getWmlObjectFactory().createDrawing();
drawing.getAnchorOrInline().add(inlineImage);
r.getRunContent().add(drawing);
p.getParagraphContent().add(r);
wordMLPackage.getMainDocumentPart().addObject(p);
// Save the document
wordMLPackage.save(new java.io.File("C:\\Users\\smokie\\DocxWithImage.docx") );
}
}
Buggy-Code:
- Code: Select all
/**
* Create an image part from the provided byte array, attach it to the source part
* (eg the main document part, a header part etc), and return it.
*
* @param wordMLPackage
* @param sourcePart
* @param bytes
* @return
* @throws Exception
*/
public static BinaryPartAbstractImage createImagePart(
WordprocessingMLPackage wordMLPackage,
Part sourcePart, byte[] bytes) throws Exception {
// Whatever image type this is, we're going to need
// to know its dimensions.
// For that we use ImageInfo, which can only
// load an image from a URI.
// So first, write the bytes to a temp file
File tmpImageFile = File.createTempFile("img", ".img");
FileOutputStream fos = new FileOutputStream(tmpImageFile);
fos.write(bytes);
fos.close();
log.debug("created tmp file: " + tmpImageFile.getAbsolutePath() );
ImageInfo info = ensureFormatIsSupported(tmpImageFile.getAbsolutePath(), tmpImageFile, bytes);
// In the absence of an exception, tmpImageFile now contains an image
// Word will accept
ContentTypeManager ctm = wordMLPackage.getContentTypeManager();
String proposedRelId = sourcePart.getRelationshipsPart().getNextId();
// In order to ensure unique part name,
// idea is to use the relId, which ought to be unique
BinaryPartAbstractImage imagePart =
(BinaryPartAbstractImage)ctm.newPartForContentType(
info.getMimeType(),
IMAGE_PREFIX + proposedRelId );
log.debug("created part " + imagePart.getClass().getName() +
" with name " + imagePart.getPartName().toString() );
FileInputStream fis = new FileInputStream(tmpImageFile); //reuse
imagePart.setBinaryData( fis );
imagePart.rel = sourcePart.addTargetPart(imagePart, proposedRelId);
imagePart.setImageInfo(info);
// Delete the tmp file
tmpImageFile.delete();
return imagePart;
}
Fix is probably (recently I have had a similar error/problem):
FileInputStream fis = new FileInputStream(tmpImageFile); //reuse
imagePart.setBinaryData( fis );
fis.close();
imagePart.rel = sourcePart.addTargetPart(imagePart, proposedRelId);
imagePart.setImageInfo(info);
// Delete the tmp file
//tmpImageFile.delete();
if (!tmpImageFile.delete()) { code which handels / reports that the file couldn't be deleted}
I would have attached a patch but I wasn't able to test the fix, because I wasn't able to build the project from source.
Thanks for developing such a nice opensource framework and I hope this post is helping to improve it.
so long
Nicola Coretti