ITextSharp - Cannot Open .pdf because it is being used by another process? - c#

I am having a issue where I write to a pdf file and then close it and later on open it up again and try to read it.
I get "Cannot Open .pdf because it is being used by another process?"
var path = // get path
Directory.CrateDirctory(path);
using(var writer = new PdfWriter(path, //writer properties)){
var reader = new PdfReader(//template);
var pdfDoc = new PdfDcoument(reader, writer);
writer.SetCloseStream(false)
// some code to fill in the pdf
reader.Close();
pdfDoc.Close();
}
//later in code
using(var stream = new MemoryStream(File.ReadAllBytes(//the file that I crated above)))
{
// do some stuff here
}
I get the error right on the using statement above. I thought all the streams for creating the pdf are closed at this point but it seems like it is not.

The issue is with the line writer.SetCloseStream(false);. That is telling it to not close the stream when the writer is closed. Since the stream is left open you will get an IOException when you create another stream for reading. Remove this line or set to true to resolve the issue.
If you need to keep the stream open for whatever reason, like issues with flushing the write buffer too soon when PdfWriter is closed. Then you can get access to the write stream and close it later when you are ready to read it. Something like this:
Stream outputStream;
using (var writer = new PdfWriter(path)){
writer.SetCloseStream(false);
// do whatever you need here
outputStream = writer.GetOutputStream();
}
// do whatever else you need here
// close the stream before creating a read stream
if(null != outputStream) {
outputStream.Close();
}
using (var stream = new MemoryStream(File.ReadAllBytes(path)))
{
// do some stuff here
}

Related

C# - Microsoft Graph API with iTextSharp - Cannot access a closed Stream

I am using Microsoft API Graph API to get a PDF file from my OneDrive which I have successfully got via this line:
var streamFile = await graphClient.Me.Drive.Items["{item-id}"].Content.Request().GetAsync();
Now I want to take a the stream of this file and edit it with iTextSharp
using (MemoryStream outFile = new MemoryStream())
{
//Dont know what to replace this with
PdfReader pdfReader = new PdfReader("Uploads/Document.pdf");
PdfStamper pdfStamper = new PdfStamper(pdfReader, outFile);
AcroFields fields = pdfStamper.AcroFields;
fields.SetField("Full_Names", "aaa");
pdfStamper.Close();
pdfReader.Close();
}
And then upload it back to OneDrive, which I am able to do via this:
//Don't know what to replace this with
var uploadPath = System.Web.HttpContext.Current.Server.MapPath("~/Uploads/NewDocument.pdf");
byte[] data = System.IO.File.ReadAllBytes(uploadPath);
Stream stream = new MemoryStream(data);
await graphClient.Me.Drive.Items["{item-id}"].ItemWithPath("NewDocument.pdf").Content.Request().PutAsync<DriveItem>(stream);
So my question is how do I take my file that I got and use iTextSharp to do its thing? So I can upload this new edited file?
UPDATE
I tried this:
var streamFile = await graphClient.Me.Drive.Items["{item-id}"].Content.Request().GetAsync();
using (MemoryStream outFile = new MemoryStream())
{
PdfReader pdfReader = new PdfReader(streamFile);
PdfStamper pdfStamper = new PdfStamper(pdfReader, outFile);
AcroFields fields = pdfStamper.AcroFields;
fields.SetField("Full_Names", "JIMMMMMMAYYYYY");
await graphClient.Me.Drive.Items["{item-id}"].ItemWithPath("NewDocument-2.pdf").Content.Request().PutAsync<DriveItem>(outFile);
pdfStamper.Close();
pdfReader.Close();
}
But got this error:
Cannot access a closed Stream.
I can see the file is being uploaded to my OneDrive, but when I goto open it I get this error:
Failed to load PDF document.
What am I doing wrong here?
UPDATE
When I remove these last two lines:
pdfStamper.Close();
pdfReader.Close();
I don't get Cannot access a closed Stream error anymore, my file uploads but I get an error when I open it:
Failed to load PDF document.
UPDATE
When I try this
var streamFile = await graphClient.Me.Drive.Items["{item-id}"].Content.Request().GetAsync();
await graphClient.Me.Drive.Items["{item-id}"].ItemWithPath("NewDocument-2.pdf").Content.Request().PutAsync<DriveItem>(streamFile);
It uploads the file I grabbed, so that part is working, but I can't edit it with iTextSharp.
See if this helps you along:
Do I need to reset a stream(C#) back to the start?
Once you read a stream, you need to reset it back to the beginning to do something else with it.

Cannot access closed stream error when trying to send data from my WCF service

On my service I am trying to create and return a MemoryStream like this:
// Write data.
MemoryStream memoryStream = new MemoryStream();
using (StreamWriter txtwriter = new StreamWriter(memoryStream, System.Text.Encoding.UTF8))
{
string tempString = "Test";
txtwriter.Write(tempString);
txtwriter.WriteLine();
// End response.
memoryStream.Position = 0L;
response.ReportMemoryStream = memoryStream;
response.Length = memoryStream.Length;
return response;
}
Note: having the End response part outside the using statement causes a Cannot access a closed stream error.
My response is a simple DataContract. When I try to read this data in my client doing something like this:
// Write stream.
if (remoteReport.ReportMemoryStream != null)
{
remoteReport.ReportMemoryStream.WriteTo(Response.OutputStream);
}
I get again the same error about the stream being closed.
How can I fix this issue and why is my stream closing even though I'm not explicitly doing so?
replace:
response.ReportMemoryStream = memoryStream;
with
memoryStream.CopyTo(response.ReportMemoryStream);
now you are using reference type I think, and your using statment is just disposing stream you are using.
You'll need to flush your StreamWriter instance, so it writes to the MemoryStream instance -
txtwriter.Flush();
Then you'll need to remove the using block, so the txtwriter doesn't by default destroyes the memoryStream.

Modify File Stream in memory

I am reading a file using StreamReader fileReader = File.OpenText(filePath). I would like to modify one line in the file in memory and push the modified stream to another method.
What I would like to avoid is reading the whole file into a string and modifying the string (doesn't scale). I would also like to avoid modifying the actual file.
Is there a straightforward way of doing this?
There is no built-in way to do that in .Net framework.
Stream and StreamReader/StreamWriter classes are designed to be chained if necessary (like GZipStream wraps stream to compress it). So you can create wrapper StreamReader and update data as you need for every operation after calling wrapped reader.
You can open two stream -one for read, one for write- at the same time. I tested simple code that works, but not sure that's what you want:
// "2.bar\r\n" will be replaced by "!!!!!\r\n"
File.WriteAllText("test.txt",
#"1.foo
2.bar
3.fake");
// open inputStream for StreamReader, and open outputStream for StreamWriter
using (var inputStream = File.Open("test.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (var reader = new StreamReader(inputStream))
using (var outputStream = File.Open("test.txt", FileMode.Open, FileAccess.Write, FileShare.Read))
using (var writer = new StreamWriter(outputStream))
{
var position = 0L; // track the reading position
var newLineLength = Environment.NewLine.Length;
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
// your particular conditions here.
if (line.StartsWith("2."))
{
// seek line start position
outputStream.Seek(position, SeekOrigin.Begin);
// replace by something,
// but the length should be equal to original in this case.
writer.WriteLine(new String('!', line.Length));
}
position += line.Length + newLineLength;
}
}
/* as a result, test.txt will be:
1.foo
!!!!!
3.fake
*/
As you can see, both streams can be accessed by StreamReader and StreamWriter at the same time. And you can also manipulate both read/write position as well.

Is PdfStamper disposing output stream? (iTextSharp)

I am using iTextSharp to add page numbers to a PDF with C#. While running code analysis the MemoryStream for the output is suspected to be disposed more than once. See this warning generated by Visual Studio. Is this an API problem? Should the second parameter of PdfStamper be marked as out? Is there a way for me to fix this warning?
MemoryStream mem = null;
PdfReader reader = null;
PdfStamper stamper = null;
try
{
mem = new MemoryStream();
reader = new PdfReader(m_pdf);
stamper = new PdfStamper(reader, mem);
// do stuff
stamper.Close();
var result = mem.ToArray();
}
finally
{
if(stamper != null)
{
stamper.Dispose();
}
if (reader != null)
{
reader.Dispose();
}
if (mem != null)
{
mem.Dispose();
}
}
This isn't really an answer but to expand upon what #mkl said, switch over to using directives since those perform the try/finally stuff for you automatically.
Below is the way I (and probably everyone else that uses iTextSharp) would generally recommend to interact with iTextSharp. The outer using is BCL stuff, in this case the MemoryStream and the inner using statements are iTextSharp stuff.
//Will hold our raw PDF bytes
Byte[] result;
//BCL stuff first
using (var mem = new MemoryStream()) {
//iText stuff in the middle
using (var reader = new PdfReader(m_pdf)) {
using (var stamper = new PdfStamper(reader, mem)) {
// do stuff
}
}
//iText is completely done and disposed of at this point
//so we can now grab the raw bytes that represent a PDF
result = mem.ToArray();
}
As an aside, not necessarily for the OP but just in case someone else sees this, there is almost never (and by "almost never" I really mean "never") a good reason to not close the underlying stream. You can read from the stream by grabbing the raw bytes and writing to it again never makes sense.
The following allowed me to keep the MemoryStream open after the stamper is closed:
pdfStamper.Writer.CloseStream = false;

StreamReader is too greedy

I'm trying to process part of a text file, and write the remainder of the text file to a cloud blob using UploadFromStream. The problem is that the StreamReader appears to be grabbing too much content from the underlying stream, and so the subsequent write does nothing.
Text file:
3
Col1,String
Col2,Integer
Col3,Boolean
abc,123,True
def,3456,False
ghijkl,532,True
mnop,1211,False
Code:
using (var stream = File.OpenRead("c:\\test\\testinput.txt"))
using (var reader = new StreamReader(stream))
{
var numColumns = int.Parse(reader.ReadLine());
while (numColumns-- > 0)
{
var colDescription = reader.ReadLine();
// do stuff
}
// Write remaining contents to another file, for testing
using (var destination = File.OpenWrite("c:\\test\\testoutput.txt"))
{
stream.CopyTo(destination);
destination.Flush();
}
// Actual intended usage:
// CloudBlockBlob blob = ...;
// blob.UploadFromStream(stream);
}
When debugging, I observe that stream.Position jumps to the end of the file on the first call to reader.ReadLine(), which I don't expect. I expected the stream to be advanced only as many positions as the reader needed to read some content.
I imagine that the stream reader is doing some buffering for performance reasons, but there doesn't seem to be a way to ask the reader where in the underlying stream it "really" is. (If there was, I could manually Seek the stream to that position before CopyingTo).
I know that I could keep taking lines using the same reader and sequentially append them to the text file I'm writing, but I'm wondering if there's a cleaner way?
EDIT:
I found a StreamReader constructor which leaves the underlying stream open when it is disposed, so I tried this, hoping that the reader would set the stream's position as it's being disposed:
using (var stream = File.OpenRead("c:\\test\\testinput.txt"))
{
using (var reader = new StreamReader(stream, Encoding.UTF8,
detectEncodingFromByteOrderMarks: true,
bufferSize: 1 << 12,
leaveOpen: true))
{
var numColumns = int.Parse(reader.ReadLine());
while (numColumns-- > 0)
{
var colDescription = reader.ReadLine();
// do stuff
}
}
// Write remaining contents to another file
using (var destination = File.OpenWrite("c:\\test\\testoutput.txt"))
{
stream.CopyTo(destination);
destination.Flush();
}
}
But it doesn't. Why would this constructor be exposed if it doesn't leave the stream in an intuitive state/position?
Sure, there's a cleaner way. Use ReadToEnd to read the remaining data, and then write it to a new file. For example:
using (var reader = new StreamReader("c:\\test\\testinput.txt"))
{
var numColumns = int.Parse(reader.ReadLine());
while (numColumns-- > 0)
{
var colDescription = reader.ReadLine();
// do stuff
}
// write everything else to another file.
File.WriteAllText("c:\\test\\testoutput.txt", reader.ReadToEnd());
}
Edit after comment
If you want to read the text and upload it to a stream, you could replace the File.WriteAllText with code that reads the remaining text, writes it to a StreamWriter backed by a MemoryStream, and then sends the contents of that MemoryStream. Something like:
using (var memStream = new MemoryStream())
{
using (var writer = new StreamWriter(memStream))
{
writer.Write(reader.ReadToEnd());
writer.Flush();
memStream.Position = 0;
blob.UploadFromStream(memStream);
}
}
You should never access the underlying stream of a StreamReader. Trying to use both is going to have an undefined behavior.
What's going on here is that the reader is buffering the data from the underlying stream. It doesn't read each byte exactly when you request it, because that's often going to be very inefficient. Instead it will grab chunks, put them in a buffer, and then provide you with data from that buffer, grabbing a new chunk when it needs to.
You should continue to use the StreamReader throughout the remainder of that block, instead of using stream. To minimize the memory footprint of the program, the most effective way of doing this would be to read the next line from the reader in a loop until it his the end of the file, writing each line to the output stream as you go.
Also note that you don't need to be disposing of both the stream reader and the underlying stream. The stream reader will dispose of the underlying stream itself, so you can simply adjust your header to:
using (var reader = new StreamReader(
File.OpenRead("c:\\test\\testinput.txt")))

Categories

Resources