Hi,
I’ve created a workaround to this problem, but it really should be fixed.
No, the problem has nothing to do with security or permissions. Here’s what’s happening…
Whenever the Workbook.Save(stream, FileFormatType.AsposePdf) is used, if the workbook contains any image files it copies these into the TEMP directory, but using a fixed naming scheme that seems to be dependant upon the type of stream. For streams associated with files, the names of these image files includes the file name. But for a MemoryStream, they look something “tmpFile.tmp_img0.jpg”. This file then must live until it is subsequently bound to an Aspose.Pdf then that object is saved to a file.
So, for example, consider the following and assume that the workbook contains some JPG image:
//***********************************************************************************************
private void CopyToPDF(Workbook workbook, Stream outputStream) {
//
// Create a temporary memory stream
using (MemoryStream memoryStream = new MemoryStream()) {
//
// Save the XLS into the memory stream as PDF XML
workbook.Save(memoryStream, FileFormatType.AsposePdf); // THIS CREATES THE TEMPORARY FILE
//
// Create a PDF
Aspose.Pdf.Pdf pdf = new Aspose.Pdf.Pdf();
//
// Bind our document
pdf.BindXML(memoryStream, null); // THIS OPENS AND LOCKS THE TEMPORARY FILE
//
// Instruct it to delete images – THIS SHOULD INSTRUCT THE Pdf OBJECT TO DELETE THE FILE BUT IT DOESN’T WORK
pdf.IsImagesInXmlDeleteNeeded = true;
//
// Save the result to the output stream
pdf.Save(outputStream);
}
//
// Force garbage collection – THIS SHOULD, BUT DOES NOT RELEASE THE FILE SINCE THE Pdf OBJECT IS OUT OF SCOPE AND SHOULD BE COLLECTED
GC.Collect();
GC.WaitForPendingFinalizers();
}
The real problem is that the temporary files created should be uniquely named. My program will have multiple threads all potentially doing exactly this same thing, and without making this code a critical region (which I don’t want to do) the threads would each be stomping on each other’s temporary files (not to mention the locked file issue).
Further, the Pdf object should implement IDisposable, and should release its resources upon Dispose(). I didn’t spend too much time on it, but even after the forced garbage collection the temporary image file is not released until some arbitrary time later. Not sure why.
Here’s a workaround. This routine goes through the intermediary XML and renames all of these temporary files to new unique names, and returns a new MemoryStream which can then be bound to a Pdf object. I’ve only tried it in my own limited scope, so anyone using this should carefully verify it works for them:
//***********************************************************************************************
static MemoryStream ChangeTemporaryImageFilenames(Stream inputStream) {
//
// Create an XmlDocument and load the contents of the stream
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.Load(inputStream);
//
// Create an XmlNamespaceManager to resolve the default namespace.
XmlNamespaceManager nsmgr = new XmlNamespaceManager(xmlDocument.NameTable);
nsmgr.AddNamespace("Aspose", "Aspose.Pdf");
//
// Loop through all File attributes of image nodes at the proper depth
int counter = 0;
foreach (XmlNode node in xmlDocument.SelectNodes(@"/Aspose:Pdf/Section/Table/Row/Cell/Image/@File", nsmgr)) {
//
// Get a FileInfo on the image file
FileInfo fileInfo = new FileInfo(node.InnerText);
//
// Create a new unique name
node.InnerText = String.Format(@"{0}\{1}-{2}-{3}{4}", fileInfo.DirectoryName, DateTime.Now.Ticks,
System.Threading.Thread.CurrentThread.ManagedThreadId, counter++, fileInfo.Extension);
//
// Move the file to the new name
fileInfo.MoveTo(node.InnerText);
}
//
// Create a new stream for the result
MemoryStream outputStream = new MemoryStream();
//
// Create an XmlTextWriter on the stream
XmlTextWriter textWriter = new XmlTextWriter(outputStream, null);
textWriter.Formatting = Formatting.Indented;
//
// Write the file out
xmlDocument.WriteTo(textWriter);
//
// Return the new stream
return outputStream;
}