DotNetZip - Cannot access a closed Stream - c#

None of the similar questions are quite what I'm looking for!
What's wrong with the following code?
files is a text array of file contents, and fileNames is the corresponding filename array.
This code always fails at the second-last line with the Save method, but I can't see why the stream would be closed!
result = new MemoryStream();
using (ZipFile zipFile = new ZipFile())
{
for (int i = 0; i < files.Count(); i++)
{
System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
Byte[] bytes = encoding.GetBytes(files[i]);
using (MemoryStream fs = new MemoryStream(bytes))
{
zipFile.AddEntry(fileNames[i], fs);
}
}
zipFile.Save(result);
}
Thanks for any help - getting desperate here!
This is my solution based on #spender's first comment, although his solution posted below is possibly nicer.
try
{
result = new MemoryStream();
List<Stream> streams = new List<Stream>();
if (files.Count > 0)
{
using (ZipFile zipFile = new ZipFile())
{
for (int i = 0; i < files.Count(); i++)
{
System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
Byte[] bytes = encoding.GetBytes(files[i]);
streams.Add(new MemoryStream(bytes));
zipFile.AddEntry(fileNames[i], streams[i]);
}
zipFile.Save(result);
}
}
}
catch (Exception ex)
{
throw;
}

It seems that calling Save is the point when the source streams are read. This means you have to keep them undisposed until after the save. Abandon using statement in this case as it is impossible to extend its scope beyond the loop. Instead, collect your IDisposables and dispose of them once the save is completed.
result = new MemoryStream();
using (ZipFile zipFile = new ZipFile())
{
List<IDisposable> memStreams = new List<IDisposable>();
try
{
for (int i = 0; i < files.Count(); i++)
{
System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
Byte[] bytes = encoding.GetBytes(files[i]);
MemoryStream fs = new MemoryStream(bytes);
zipFile.AddEntry(fileNames[i], fs);
memStreams.Add(fs);
}
zipFile.Save(result);
}
finally
{
foreach(var x in memStreams)
{
x.Dispose();
}
}
}

#spender answer is spot on but as my 2 cents if you want to be more synonymous with using pattern you can do something like
class
public class DisposableBucket : IDisposable
{
readonly List<IDisposable> listOfDisposables = new List<IDisposable>();
public TClass Using<TClass>(TClass disposable) where TClass : IDisposable
{
listOfDisposables.Add(disposable);
return disposable;
}
public void Dispose()
{
foreach (var listOfDisposable in listOfDisposables)
{
listOfDisposable.Dispose();
}
}
}
How to use
result = new MemoryStream();
using(var bucket = new DisposableBucket())
{
using (var zipFile = new ZipFile())
{
List<IDisposable> memStreams = new List<IDisposable>();
for (int i = 0; i < files.Count(); i++)
{
System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
Byte[] bytes = encoding.GetBytes(files[i]);
var fs = bucket.Using(new MemoryStream(bytes));
zipFile.AddEntry(fileNames[i], fs);
}
zipFile.Save(result);
}
}

Related

Aspose PDF Merge PDF from byte[][]

I'm working on a service that pulls multiple PDF files stored in a database as varbinary and merges them into a single file using Aspose - PDF. The merged file is then converted to a Memory Stream and then a blob and then sent to the webpage.
Here's my service:
public MemoryStream GetPrintContent(List<ConfirmationRequestNoticeViewModel> models)
{
// Instantiate Pdf instance by calling its empty constructor
Document pdf1 = new Document();
byte[][] bytes = new byte[models.Count][];
for (int i = 0; i < models.Count; i++)
{
ConfirmationRequestNoticeViewModel model = models[i];
byte[] fileContent = _dataService.GetPrintContent(model.ConfirmationRequestId);
bytes[i] = fileContent;
}
MemoryStream stream = new MemoryStream();
List<Document> documents = GeneratePdfs(bytes, stream);
stream = ConcatenatePdf(documents);
return stream;
}
private MemoryStream ConcatenatePdf(List<Document> documents)
{
MemoryStream stream = new MemoryStream();
Document mergedPdf = documents[0];
for(int index = 1; index < documents.Count; index++)
{
for (int i = 0; i < documents[index].Pages.Count; i++)
{
mergedPdf.Pages.Add(documents[index].Pages[i + 1]);
}
}
mergedPdf.Save(stream);
return stream;
}
private List<Document> GeneratePdfs(byte[][] content, MemoryStream stream)
{
List<Document> documents = new List<Document>();
Document pdf = new Document();
foreach (byte[] fileContent in content)
{
using (MemoryStream fileStream = new MemoryStream(fileContent))
{
pdf = new Document(fileStream);
pdf.Save(stream);
documents.Add(pdf);
}
}
return documents;
}
Everything is working great except for the line mergedPdf.Save(stream); which returns the error Cannot access a closed stream.
I've been working on this and can't seem to understand why the Memory Stream has been closed. Has anyone else encountered this issue?
Edit:
I've found the issue listed here
I couldn't solve the issue with closed MemoryStreams with the current implementation so I had to refactor completely.
Instead, I went with the PdfFileEditor.Concatenate() method explained in this forum post.
My implementation is as follows:
public byte[] GetPrintContent(List<ConfirmationRequestNoticeViewModel> models)
{
PdfFileEditor pdfEditor = new PdfFileEditor();
MemoryStream[] inputStreams = new MemoryStream[models.Count];
MemoryStream fileStream = new MemoryStream(); ;
using (MemoryStream outputStream = new MemoryStream())
{
for (int i = 0; i < models.Count; i++)
{
ConfirmationRequestNoticeViewModel model = models[i];
byte[] fileContent = _dataService.GetPrintContent(model.ConfirmationRequestId);
fileStream = new MemoryStream(fileContent);
inputStreams[i] = fileStream;
}
bool success = pdfEditor.Concatenate(inputStreams, outputStream);
byte[] data = outputStream.ToArray();
fileStream.Dispose();
return data;
}
}
For the closed memory Stream issue, you should remove the using(), as it disposes the stream while coming out of {}. It will work fine.

Create zip as byte[] in memory without saving to disk? [duplicate]

I'm trying to create a ZIP archive with a simple demo text file using a MemoryStream as follows:
using (var memoryStream = new MemoryStream())
using (var archive = new ZipArchive(memoryStream , ZipArchiveMode.Create))
{
var demoFile = archive.CreateEntry("foo.txt");
using (var entryStream = demoFile.Open())
using (var streamWriter = new StreamWriter(entryStream))
{
streamWriter.Write("Bar!");
}
using (var fileStream = new FileStream(#"C:\Temp\test.zip", FileMode.Create))
{
stream.CopyTo(fileStream);
}
}
If I run this code, the archive file itself is created but foo.txt isn't.
However, if I replace the MemoryStream directly with the file stream, the archive is created correctly:
using (var fileStream = new FileStream(#"C:\Temp\test.zip", FileMode.Create))
using (var archive = new ZipArchive(fileStream, FileMode.Create))
{
// ...
}
Is it possible to use a MemoryStream to create the ZIP archive without the FileStream?
Thanks to ZipArchive creates invalid ZIP file, I got:
using (var memoryStream = new MemoryStream())
{
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
{
var demoFile = archive.CreateEntry("foo.txt");
using (var entryStream = demoFile.Open())
using (var streamWriter = new StreamWriter(entryStream))
{
streamWriter.Write("Bar!");
}
}
using (var fileStream = new FileStream(#"C:\Temp\test.zip", FileMode.Create))
{
memoryStream.Seek(0, SeekOrigin.Begin);
memoryStream.CopyTo(fileStream);
}
}
That indicated we need to call Dispose on ZipArchive before we can use it, which as Amir suggests is likely because it writes final bytes like checksum to the archive that makes it complete. But in order not close the stream so we can re-use it after you need to pass true as the third parameter to ZipArchive.
Just another version of zipping without writing any file.
string fileName = "export_" + DateTime.Now.ToString("yyyyMMddhhmmss") + ".xlsx";
byte[] fileBytes = here is your file in bytes
byte[] compressedBytes;
string fileNameZip = "Export_" + DateTime.Now.ToString("yyyyMMddhhmmss") + ".zip";
using (var outStream = new MemoryStream())
{
using (var archive = new ZipArchive(outStream, ZipArchiveMode.Create, true))
{
var fileInArchive = archive.CreateEntry(fileName, CompressionLevel.Optimal);
using (var entryStream = fileInArchive.Open())
using (var fileToCompressStream = new MemoryStream(fileBytes))
{
fileToCompressStream.CopyTo(entryStream);
}
}
compressedBytes = outStream.ToArray();
}
Set the position of the stream to the 0 before copying it to the zip stream.
using (var memoryStream = new MemoryStream())
{
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
{
var demoFile = archive.CreateEntry("foo.txt");
using (var entryStream = demoFile.Open())
using (var streamWriter = new StreamWriter(entryStream))
{
streamWriter.Write("Bar!");
}
}
using (var fileStream = new FileStream(#"C:\Temp\test.zip", FileMode.Create))
{
memoryStream.Position=0;
memoryStream.WriteTo(fileStream);
}
}
Working solution for MVC
public ActionResult Index()
{
string fileName = "test.pdf";
string fileName1 = "test.vsix";
string fileNameZip = "Export_" + DateTime.Now.ToString("yyyyMMddhhmmss") + ".zip";
byte[] fileBytes = System.IO.File.ReadAllBytes(#"C:\test\test.pdf");
byte[] fileBytes1 = System.IO.File.ReadAllBytes(#"C:\test\test.vsix");
byte[] compressedBytes;
using (var outStream = new MemoryStream())
{
using (var archive = new ZipArchive(outStream, ZipArchiveMode.Create, true))
{
var fileInArchive = archive.CreateEntry(fileName, CompressionLevel.Optimal);
using (var entryStream = fileInArchive.Open())
using (var fileToCompressStream = new MemoryStream(fileBytes))
{
fileToCompressStream.CopyTo(entryStream);
}
var fileInArchive1 = archive.CreateEntry(fileName1, CompressionLevel.Optimal);
using (var entryStream = fileInArchive1.Open())
using (var fileToCompressStream = new MemoryStream(fileBytes1))
{
fileToCompressStream.CopyTo(entryStream);
}
}
compressedBytes = outStream.ToArray();
}
return File(compressedBytes, "application/zip", fileNameZip);
}
You need to finish writing the memory stream then read the buffer back.
using (var memoryStream = new MemoryStream())
{
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create))
{
var demoFile = archive.CreateEntry("foo.txt");
using (var entryStream = demoFile.Open())
using (var streamWriter = new StreamWriter(entryStream))
{
streamWriter.Write("Bar!");
}
}
using (var fileStream = new FileStream(#"C:\Temp\test.zip", FileMode.Create))
{
var bytes = memoryStream.GetBuffer();
fileStream.Write(bytes,0,bytes.Length );
}
}
using System;
using System.IO;
using System.IO.Compression;
namespace ConsoleApplication
{
class Program`enter code here`
{
static void Main(string[] args)
{
using (FileStream zipToOpen = new FileStream(#"c:\users\exampleuser\release.zip", FileMode.Open))
{
using (ZipArchive archive = new ZipArchive(zipToOpen, ZipArchiveMode.Update))
{
ZipArchiveEntry readmeEntry = archive.CreateEntry("Readme.txt");
using (StreamWriter writer = new StreamWriter(readmeEntry.Open()))
{
writer.WriteLine("Information about this package.");
writer.WriteLine("========================");
}
}
}
}
}
}
Function to return stream that contain zip file
public static Stream ZipGenerator(List<string> files)
{
ZipArchiveEntry fileInArchive;
Stream entryStream;
int i = 0;
List<byte[]> byteArray = new List<byte[]>();
foreach (var file in files)
{
byteArray.Add(File.ReadAllBytes(file));
}
var outStream = new MemoryStream();
using (var archive = new ZipArchive(outStream, ZipArchiveMode.Create, true))
{
foreach (var file in files)
{
fileInArchive=(archive.CreateEntry(Path.GetFileName(file), CompressionLevel.Optimal));
using (entryStream = fileInArchive.Open())
{
using (var fileToCompressStream = new MemoryStream(byteArray[i]))
{
fileToCompressStream.CopyTo(entryStream);
}
i++;
}
}
}
outStream.Position = 0;
return outStream;
}
If you want , write zip to file stream.
using (var fileStream = new FileStream(#"D:\Tools\DBExtractor\DBExtractor\bin\Debug\test.zip", FileMode.Create))
{
outStream.Position = 0;
outStream.WriteTo(fileStream);
}
`
I'm late to the party, but there are scenarios where you can't access the ZipArchive's constructor to set the leaveOpen parameter and where you don't want the ZIP to be written to disk. In my case, the AsiceArchive class I'm using internally creates a ZipArchive but doesn't set leaveOpen to true.
I created a subclass of Stream that delegates all calls to an inner stream (a few clicks with ReSharper). This class is not disposable, so when the ZipArchive gets disposed, nothing happens to the inner stream.
public class NondisposingStreamWrapper : Stream
{
private readonly Stream _streamImplementation;
public NondisposingStreamWrapper(Stream inner) => _streamImplementation = inner;
public override void Flush() => _streamImplementation.Flush();
public override int Read(byte[] buffer, int offset, int count) => _streamImplementation.Read(buffer, offset, count);
public override long Seek(long offset, SeekOrigin origin) => _streamImplementation.Seek(offset, origin);
public override void SetLength(long value) => _streamImplementation.SetLength(value);
public override void Write(byte[] buffer, int offset, int count) => _streamImplementation.Write(buffer, offset, count);
public override bool CanRead => _streamImplementation.CanRead;
public override bool CanSeek => _streamImplementation.CanSeek;
public override bool CanWrite => _streamImplementation.CanWrite;
public override long Length => _streamImplementation.Length;
public override long Position
{
get => _streamImplementation.Position;
set => _streamImplementation.Position = value;
}
}
Use it like this:
using var memoryStream = new MemoryStream();
var output = new NondisposingStreamWrapper(memoryStream);
using (var archive = new ZipArchive(output, ZipArchiveMode.Create))
{
// add entries to archive
}
memoryStream.Flush();
memoryStream.Position = 0;
// write to file just for testing purposes
File.WriteAllBytes("out.zip", memoryStream.ToArray());
This is the way to convert a entity to XML File and then compress it:
private void downloadFile(EntityXML xml) {
string nameDownloadXml = "File_1.xml";
string nameDownloadZip = "File_1.zip";
var serializer = new XmlSerializer(typeof(EntityXML));
Response.Clear();
Response.ClearContent();
Response.ClearHeaders();
Response.AddHeader("content-disposition", "attachment;filename=" + nameDownloadZip);
using (var memoryStream = new MemoryStream())
{
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
{
var demoFile = archive.CreateEntry(nameDownloadXml);
using (var entryStream = demoFile.Open())
using (StreamWriter writer = new StreamWriter(entryStream, System.Text.Encoding.UTF8))
{
serializer.Serialize(writer, xml);
}
}
using (var fileStream = Response.OutputStream)
{
memoryStream.Seek(0, SeekOrigin.Begin);
memoryStream.CopyTo(fileStream);
}
}
Response.End();
}
Just in case, if anyone wants to save a dynamic zip file through SaveFileDialog.
var logFileName = "zip_filename.zip";
appLogSaver.FileName = logFileName;
appLogSaver.Filter = "LogFiles|*.zip";
appLogSaver.DefaultExt = "zip";
DialogResult resDialog = appLogSaver.ShowDialog();
if (resDialog.ToString() == "OK")
{
System.IO.FileStream fs = (System.IO.FileStream)appLogSaver.OpenFile();
using (var memoryStream = new MemoryStream())
{
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
{
var demoFile = archive.CreateEntry("foo.txt");
using (var entryStream = demoFile.Open())
{
using (var streamWriter = new StreamWriter(entryStream))
{
//read your existing file and put the content here
streamWriter.Write("Bar!");
}
}
var demoFile2 = archive.CreateEntry("foo2.txt");
using (var entryStream = demoFile2.Open())
{
using (var streamWriter = new StreamWriter(entryStream))
{
streamWriter.Write("Bar2!");
}
}
}
memoryStream.Seek(0, SeekOrigin.Begin);
memoryStream.CopyTo(fs);
}
fs.Close();
}
For me something like this was ok:
using (var memoryStream = new MemoryStream())
{
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
{
var file = archive.CreateEntry("file.json");
using var entryStream = file.Open();
using var streamWriter = new StreamWriter(entryStream);
streamWriter.WriteLine(someJsonLine);
}
}
private void button6_Click(object sender, EventArgs e)
{
//create With Input FileNames
AddFileToArchive_InputByte(new ZipItem[]{ new ZipItem( #"E:\b\1.jpg",#"images\1.jpg"),
new ZipItem(#"E:\b\2.txt",#"text\2.txt")}, #"C:\test.zip");
//create with input stream
AddFileToArchive_InputByte(new ZipItem[]{ new ZipItem(File.ReadAllBytes( #"E:\b\1.jpg"),#"images\1.jpg"),
new ZipItem(File.ReadAllBytes(#"E:\b\2.txt"),#"text\2.txt")}, #"C:\test.zip");
//Create Archive And Return StreamZipFile
MemoryStream GetStreamZipFile = AddFileToArchive(new ZipItem[]{ new ZipItem( #"E:\b\1.jpg",#"images\1.jpg"),
new ZipItem(#"E:\b\2.txt",#"text\2.txt")});
//Extract in memory
ZipItem[] ListitemsWithBytes = ExtractItems(#"C:\test.zip");
//Choese Files For Extract To memory
List<string> ListFileNameForExtract = new List<string>(new string[] { #"images\1.jpg", #"text\2.txt" });
ListitemsWithBytes = ExtractItems(#"C:\test.zip", ListFileNameForExtract);
// Choese Files For Extract To Directory
ExtractItems(#"C:\test.zip", ListFileNameForExtract, "c:\\extractFiles");
}
public struct ZipItem
{
string _FileNameSource;
string _PathinArchive;
byte[] _Bytes;
public ZipItem(string __FileNameSource, string __PathinArchive)
{
_Bytes=null ;
_FileNameSource = __FileNameSource;
_PathinArchive = __PathinArchive;
}
public ZipItem(byte[] __Bytes, string __PathinArchive)
{
_Bytes = __Bytes;
_FileNameSource = "";
_PathinArchive = __PathinArchive;
}
public string FileNameSource
{
set
{
FileNameSource = value;
}
get
{
return _FileNameSource;
}
}
public string PathinArchive
{
set
{
_PathinArchive = value;
}
get
{
return _PathinArchive;
}
}
public byte[] Bytes
{
set
{
_Bytes = value;
}
get
{
return _Bytes;
}
}
}
public void AddFileToArchive(ZipItem[] ZipItems, string SeveToFile)
{
MemoryStream memoryStream = new MemoryStream();
//Create Empty Archive
ZipArchive archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true);
foreach (ZipItem item in ZipItems)
{
//Create Path File in Archive
ZipArchiveEntry FileInArchive = archive.CreateEntry(item.PathinArchive);
//Open File in Archive For Write
var OpenFileInArchive = FileInArchive.Open();
//Read Stream
FileStream fsReader = new FileStream(item.FileNameSource, FileMode.Open, FileAccess.Read);
byte[] ReadAllbytes = new byte[4096];//Capcity buffer
int ReadByte = 0;
while (fsReader.Position != fsReader.Length)
{
//Read Bytes
ReadByte = fsReader.Read(ReadAllbytes, 0, ReadAllbytes.Length);
//Write Bytes
OpenFileInArchive.Write(ReadAllbytes, 0, ReadByte);
}
fsReader.Dispose();
OpenFileInArchive.Close();
}
archive.Dispose();
using (var fileStream = new FileStream(SeveToFile, FileMode.Create))
{
memoryStream.Seek(0, SeekOrigin.Begin);
memoryStream.CopyTo(fileStream);
}
}
public MemoryStream AddFileToArchive(ZipItem[] ZipItems)
{
MemoryStream memoryStream = new MemoryStream();
//Create Empty Archive
ZipArchive archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true);
foreach (ZipItem item in ZipItems)
{
//Create Path File in Archive
ZipArchiveEntry FileInArchive = archive.CreateEntry(item.PathinArchive);
//Open File in Archive For Write
var OpenFileInArchive = FileInArchive.Open();
//Read Stream
FileStream fsReader = new FileStream(item.FileNameSource, FileMode.Open, FileAccess.Read);
byte[] ReadAllbytes = new byte[4096];//Capcity buffer
int ReadByte = 0;
while (fsReader.Position != fsReader.Length)
{
//Read Bytes
ReadByte = fsReader.Read(ReadAllbytes, 0, ReadAllbytes.Length);
//Write Bytes
OpenFileInArchive.Write(ReadAllbytes, 0, ReadByte);
}
fsReader.Dispose();
OpenFileInArchive.Close();
}
archive.Dispose();
return memoryStream;
}
public void AddFileToArchive_InputByte(ZipItem[] ZipItems, string SeveToFile)
{
MemoryStream memoryStream = new MemoryStream();
//Create Empty Archive
ZipArchive archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true);
foreach (ZipItem item in ZipItems)
{
//Create Path File in Archive
ZipArchiveEntry FileInArchive = archive.CreateEntry(item.PathinArchive);
//Open File in Archive For Write
var OpenFileInArchive = FileInArchive.Open();
//Read Stream
// FileStream fsReader = new FileStream(item.FileNameSource, FileMode.Open, FileAccess.Read);
byte[] ReadAllbytes = new byte[4096];//Capcity buffer
int ReadByte = 4096 ;int TotalWrite=0;
while (TotalWrite != item.Bytes.Length)
{
if(TotalWrite+4096>item.Bytes.Length)
ReadByte=item.Bytes.Length-TotalWrite;
Array.Copy(item.Bytes, TotalWrite, ReadAllbytes, 0, ReadByte);
//Write Bytes
OpenFileInArchive.Write(ReadAllbytes, 0, ReadByte);
TotalWrite += ReadByte;
}
OpenFileInArchive.Close();
}
archive.Dispose();
using (var fileStream = new FileStream(SeveToFile, FileMode.Create))
{
memoryStream.Seek(0, SeekOrigin.Begin);
memoryStream.CopyTo(fileStream);
}
}
public MemoryStream AddFileToArchive_InputByte(ZipItem[] ZipItems)
{
MemoryStream memoryStream = new MemoryStream();
//Create Empty Archive
ZipArchive archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true);
foreach (ZipItem item in ZipItems)
{
//Create Path File in Archive
ZipArchiveEntry FileInArchive = archive.CreateEntry(item.PathinArchive);
//Open File in Archive For Write
var OpenFileInArchive = FileInArchive.Open();
//Read Stream
// FileStream fsReader = new FileStream(item.FileNameSource, FileMode.Open, FileAccess.Read);
byte[] ReadAllbytes = new byte[4096];//Capcity buffer
int ReadByte = 4096 ;int TotalWrite=0;
while (TotalWrite != item.Bytes.Length)
{
if(TotalWrite+4096>item.Bytes.Length)
ReadByte=item.Bytes.Length-TotalWrite;
Array.Copy(item.Bytes, TotalWrite, ReadAllbytes, 0, ReadByte);
//Write Bytes
OpenFileInArchive.Write(ReadAllbytes, 0, ReadByte);
TotalWrite += ReadByte;
}
OpenFileInArchive.Close();
}
archive.Dispose();
return memoryStream;
}
public void ExtractToDirectory(string sourceArchiveFileName, string destinationDirectoryName)
{
//Opens the zip file up to be read
using (ZipArchive archive = ZipFile.OpenRead(sourceArchiveFileName))
{
if (Directory.Exists(destinationDirectoryName)==false )
Directory.CreateDirectory(destinationDirectoryName);
//Loops through each file in the zip file
archive.ExtractToDirectory(destinationDirectoryName);
}
}
public void ExtractItems(string sourceArchiveFileName,List< string> _PathFilesinArchive, string destinationDirectoryName)
{
//Opens the zip file up to be read
using (ZipArchive archive = ZipFile.OpenRead(sourceArchiveFileName))
{
//Loops through each file in the zip file
foreach (ZipArchiveEntry file in archive.Entries)
{
int PosResult = _PathFilesinArchive.IndexOf(file.FullName);
if (PosResult != -1)
{
//Create Folder
if (Directory.Exists( destinationDirectoryName + "\\" +Path.GetDirectoryName( _PathFilesinArchive[PosResult])) == false)
Directory.CreateDirectory(destinationDirectoryName + "\\" + Path.GetDirectoryName(_PathFilesinArchive[PosResult]));
Stream OpenFileGetBytes = file.Open();
FileStream FileStreamOutput = new FileStream(destinationDirectoryName + "\\" + _PathFilesinArchive[PosResult], FileMode.Create);
byte[] ReadAllbytes = new byte[4096];//Capcity buffer
int ReadByte = 0; int TotalRead = 0;
while (TotalRead != file.Length)
{
//Read Bytes
ReadByte = OpenFileGetBytes.Read(ReadAllbytes, 0, ReadAllbytes.Length);
TotalRead += ReadByte;
//Write Bytes
FileStreamOutput.Write(ReadAllbytes, 0, ReadByte);
}
FileStreamOutput.Close();
OpenFileGetBytes.Close();
_PathFilesinArchive.RemoveAt(PosResult);
}
if (_PathFilesinArchive.Count == 0)
break;
}
}
}
public ZipItem[] ExtractItems(string sourceArchiveFileName)
{
List< ZipItem> ZipItemsReading = new List<ZipItem>();
//Opens the zip file up to be read
using (ZipArchive archive = ZipFile.OpenRead(sourceArchiveFileName))
{
//Loops through each file in the zip file
foreach (ZipArchiveEntry file in archive.Entries)
{
Stream OpenFileGetBytes = file.Open();
MemoryStream memstreams = new MemoryStream();
byte[] ReadAllbytes = new byte[4096];//Capcity buffer
int ReadByte = 0; int TotalRead = 0;
while (TotalRead != file.Length)
{
//Read Bytes
ReadByte = OpenFileGetBytes.Read(ReadAllbytes, 0, ReadAllbytes.Length);
TotalRead += ReadByte;
//Write Bytes
memstreams.Write(ReadAllbytes, 0, ReadByte);
}
memstreams.Position = 0;
OpenFileGetBytes.Close();
memstreams.Dispose();
ZipItemsReading.Add(new ZipItem(memstreams.ToArray(),file.FullName));
}
}
return ZipItemsReading.ToArray();
}
public ZipItem[] ExtractItems(string sourceArchiveFileName,List< string> _PathFilesinArchive)
{
List< ZipItem> ZipItemsReading = new List<ZipItem>();
//Opens the zip file up to be read
using (ZipArchive archive = ZipFile.OpenRead(sourceArchiveFileName))
{
//Loops through each file in the zip file
foreach (ZipArchiveEntry file in archive.Entries)
{
int PosResult = _PathFilesinArchive.IndexOf(file.FullName);
if (PosResult!= -1)
{
Stream OpenFileGetBytes = file.Open();
MemoryStream memstreams = new MemoryStream();
byte[] ReadAllbytes = new byte[4096];//Capcity buffer
int ReadByte = 0; int TotalRead = 0;
while (TotalRead != file.Length)
{
//Read Bytes
ReadByte = OpenFileGetBytes.Read(ReadAllbytes, 0, ReadAllbytes.Length);
TotalRead += ReadByte;
//Write Bytes
memstreams.Write(ReadAllbytes, 0, ReadByte);
}
//Create item
ZipItemsReading.Add(new ZipItem(memstreams.ToArray(),file.FullName));
OpenFileGetBytes.Close();
memstreams.Dispose();
_PathFilesinArchive.RemoveAt(PosResult);
}
if (_PathFilesinArchive.Count == 0)
break;
}
}
return ZipItemsReading.ToArray();
}

Concatenating PDF using PDFsharp returns empty PDF

I have a list of PDF stored as list<byte[]>. I try to concatenate all these PDF files using PDFsharp, but after my operation I get a PDF with proper page count, but all pages are blank. Looks like I lose some header or something but I can't find where.
My code:
PdfDocument output = new PdfDocument();
try
{
foreach (var report in reports)
{
using (MemoryStream stream = new MemoryStream(report))
{
PdfDocument input = PdfReader.Open(stream, PdfDocumentOpenMode.Import);
foreach (PdfPage page in input.Pages)
{
output.AddPage(page);
}
}
}
if (output.Pages.Count <= 0)
{
throw new Exception("Empty Document");
}
MemoryStream final = new MemoryStream();
output.Save(final);
output.Close();
return final.ToArray();
}
catch (Exception e)
{
throw new Exception(e.ToString());
}
I want to return it as byte[] because I use them later:
return File(report, System.Net.Mime.MediaTypeNames.Application.Octet, "test.pdf");
This returns PDF with proper page count, but all blank.
You tell in a comment that the files come from SSRS.
Older versions of PDFsharp require a special SSRS setting:
For the DeviceSettings parameter for the Render method on the ReportExecutionService object, pass this value:
theDeviceSettings = "<DeviceInfo><HumanReadablePDF>True</HumanReadablePDF></DeviceInfo>";
Source:
http://forum.pdfsharp.net/viewtopic.php?p=1613#p1613
I use iTextSharp, look at this saple code (it works)
public static byte[] PdfJoin(List<String> pdfs)
{
byte[] mergedPdf = null;
using (MemoryStream ms = new MemoryStream())
{
using (iTextSharp.text.Document document = new iTextSharp.text.Document())
{
using (iTextSharp.text.pdf.PdfCopy copy = new iTextSharp.text.pdf.PdfCopy(document, ms))
{
document.Open();
for (int i = 0; i < pdfs.Count; ++i)
{
iTextSharp.text.pdf.PdfReader reader = new iTextSharp.text.pdf.PdfReader(pdfs[i]);
// loop over the pages in that document
int n = reader.NumberOfPages;
for (int page = 0; page < n; )
{
copy.AddPage(copy.GetImportedPage(reader, ++page));
}
}
}
}
mergedPdf = ms.ToArray();
}
return mergedPdf;
}
public static byte[] PdfJoin(List<byte[]> pdfs)
{
byte[] mergedPdf = null;
using (MemoryStream ms = new MemoryStream())
{
using (iTextSharp.text.Document document = new iTextSharp.text.Document())
{
using (iTextSharp.text.pdf.PdfCopy copy = new iTextSharp.text.pdf.PdfCopy(document, ms))
{
document.Open();
for (int i = 0; i < pdfs.Count; ++i)
{
iTextSharp.text.pdf.PdfReader reader = new iTextSharp.text.pdf.PdfReader(pdfs[i]);
// loop over the pages in that document
int n = reader.NumberOfPages;
for (int page = 0; page < n; )
{
copy.AddPage(copy.GetImportedPage(reader, ++page));
}
}
}
}
mergedPdf = ms.ToArray();
}
return mergedPdf;
}

Append to file using BinaryFormatter

I'm using this code
for (int i = 0; i < 3; ++i)
{
List<int> tl = new List<int>();
tl.Add(5);
tl.Add(4);
using (var fileStream = new FileStream(#"C:\file.dat", FileMode.Append))
{
var bFormatter = new BinaryFormatter();
bFormatter.Serialize(fileStream, tl);
//fileStream.Close();
}
var list = new List<int>();
using (var fileStream = new FileStream(#"C:\file.dat", FileMode.Open))
{
var bFormatter = new BinaryFormatter();
//while (fileStream.Position != fileStream.Length)
//{
// list.Add((int)bFormatter.Deserialize(fileStream));
//}
list = (List<int>)bFormatter.Deserialize(fileStream);
//fileStream.Close();
}
}
I expect .dat file will be
5 4 5 4 5 4
but it's only
5 4
also this code return also
5 4
List<int> tl = new List<int>();
tl.Add(5);
tl.Add(4);
using (var fileStream = new FileStream(#"C:\file.dat", FileMode.Append))
{
var bFormatter = new BinaryFormatter();
bFormatter.Serialize(fileStream, tl);
}
tl.Clear();
tl.Add(3);
tl.Add(2);
using (var fileStream = new FileStream(#"C:\file.dat", FileMode.Append))
{
var bFormatter = new BinaryFormatter();
bFormatter.Serialize(fileStream, tl);
}
var list = new List<int>();
using (var fileStream = new FileStream(#"C:\file.dat", FileMode.Open))
{
var bFormatter = new BinaryFormatter();
list = (List<int>)bFormatter.Deserialize(fileStream);
}
it looks like it de-serialize only first portion that was appended.
why data don't append?
UPDATE:
So the solution is:
var list = new List<int>();
using (var fileStream = new FileStream(#"C:\file.dat", FileMode.Open))
{
var bFormatter = new BinaryFormatter();
while (fileStream.Position != fileStream.Length)
{
var t = (List<int>)(bFormatter.Deserialize(fileStream));
list.AddRange(t);
}
}
You are adding three lists of ints, one after the other, and only reading the first one back. I think what your intent may be is to append to a (single) existing list, in which case you'd have to
Read your list back into memory
Add your new elements
Write the list back out to the file in overwrite (not append) mode
BinaryFormatter is not listed as being appendable. As it happens, you can usually get away with deserializing multiple times until you get to the EOF (and merging manually), but: there are other serializers which are explicitly designed to be appendable. For example, protocol buffers is an appendable format: concatenation is identical to merge. Further: if the outer element is a list, appending to the file is identical to adding to the composes list.
With protobuf-net, this is just:
for (int i = 0; i < 3; ++i)
{
List<int> tl = new List<int>();
tl.Add(5);
tl.Add(4);
using (var fileStream = new FileStream(#"C:\file.dat", FileMode.Append))
{
Serializer.Serialize(fileStream, tl);
}
using (var fileStream = new FileStream(#"C:\file.dat", FileMode.Open))
{
list = Serializer.Deserialize<List<int>>(fileStream);
}
}
At the end of each loop iteration, list (i.e. after deserialization) has 2, then 4, then 6 elements.
As Marc Gravell mentioned BinaryFormatter isn't appendable which means you'll need to reserialize every time you need to modify the file.
Example:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
class Program
{
const string file = #"C:\temp\file.dat";
static void Main()
{
for (int i = 0; i < 3; ++i)
{
List<int> tl = new List<int>();
tl.Add(5);
tl.Add(4);
AppendToDisk(tl);
}
var list = ReadFromDisk<int>();
foreach (var item in list)
{
Console.Write(item);
}
}
private static void AppendToDisk<T>(IEnumerable<T> collection)
{
var existing = ReadFromDisk<T>().ToList();
existing.AddRange(collection);
PersistToDisk(existing);
}
private static void PersistToDisk<T>(ICollection<T> value)
{
if (!File.Exists(file))
{
using (File.Create(file)) { };
}
var bFormatter = new BinaryFormatter();
using (var stream = File.OpenWrite(file))
{
bFormatter.Serialize(stream, value);
}
}
private static ICollection<T> ReadFromDisk<T>()
{
if (!File.Exists(file)) return Enumerable.Empty<T>().ToArray();
var bFormatter = new BinaryFormatter();
using (var stream = File.OpenRead(file))
{
return (ICollection<T>)bFormatter.Deserialize(stream);
}
}
}

Why StringWriter.ToString return `System.Byte[]` and not the data?

UnZipFile method writes the data from inputStream to outputWriter.
Why sr.ToString() returns System.Byte[] and not the data?
using (var sr = new StringWriter())
{
UnZipFile(response.GetResponseStream(), sr);
var content = sr.ToString();
}
public static void UnZipFile(Stream inputStream, TextWriter outputWriter)
{
using (var zipStream = new ZipInputStream(inputStream))
{
ZipEntry currentEntry;
if ((currentEntry = zipStream.GetNextEntry()) != null)
{
var size = 2048;
var data = new byte[size];
while (true)
{
size = zipStream.Read(data, 0, size);
if (size > 0)
{
outputWriter.Write(data);
}
else
{
break;
}
}
}
}
}
The problem is on the line:
outputWriter.Write(data);
StringWriter.Write has no overload expecting a byte[]. Therefore, Write(Object) is called instead. And according to MSDN:
Writes the text representation of an object to the text string or stream by calling the ToString method on that object.
Calling ToString on a byte array returns System.byte[], explaining how you get that string in your StringWriter.
The reason is simple:
data is of type byte[]. There is no overload for byte[] on StringWriter so it uses the overload for object. And then calls ToString() on the boxed byte array which simply prints the type.
Your code is equivalent to this:
outputWriter.Write(data.ToString());
theateist,
Looking at the other answers here, I am going to have to agree that the reason for the "ToString()" returning System.Byte[] is because that is what you are putting into it, and everything put into the StringWriter calls it's own "ToString" method when doing so. (i.e. byte[].toString() = "System.byte[]"). In fact the whole idea is that the StringWriter is only ment for writing into a string "buffer" (StringBuilder), so in theory if your file was large enough(bigger than 2048), your output would be "System.Byte[]System.Byte[]" (etc.). Try this to deflate into a memory stream and then read from that stream, may be a better understanding of what you are looking at. (Code not tested, just example).
using (Stream ms = new MemoryStream())
{
UnZipFile(response.GetResponseStream(), ms);
string content;
ms.Position = 0;
using(StreamReader s = new StreamReader(ms))
{
content = s.ReadToEnd();
}
}
public static void UnZipFile(Stream inputStream, Stream outputWriter)
{
using (var zipStream = new ZipInputStream(inputStream))
{
ZipEntry currentEntry;
if ((currentEntry = zipStream.GetNextEntry()) != null)
{
int size = 2048;
byte[] data = new byte[size];
while (true)
{
size = zipStream.Read(data, 0, size);
if (size > 0)
{
outputWriter.Write(data);
}
else
{
break;
}
}
}
}
}
Another idea would actually be to using the endcoding to get the string
public string UnZipFile(Stream inputStream)
{
string tmp;
using(Stream zipStream = new ZipInputStream(inputStream))
{
ZipEntry currentEntry;
if(currentEntry = zipStream.GetNextEntry()) != null)
{
using(Stream ms = new MemoryStream())
{
int size = 2048;
byte[] data = new byte[size];
while(true)
{
if((size = zipStream.Read(data,0,size)) > 0)
ms.Write(data);
else
break;
}
tmp = Encoding.Default.GetString(ms.ToByteArray());
}
}
}
}
return tmp;
}
Or as one last idea, you could actually change your original code to have
outputWriter.Write(Encoding.Default.GetString(data));
Instead of
outputWriter.Write(data);
By the way, please avoid the var keyword in posts, maybe just my pet peev, but code is less readable when utilizing weak types.
StringWriter.Write:MSDN
StringWriter.ToString:MSDN

Categories