Creating ZIP on the fly produces corrupted file - c#

I'm trying to make some sort of download service for a school project. Concept is simple - create a zip file from uploaded files. First problem is asp.net on school server is configured that apps cannot write any files, so I'm storing them in database. Then I need to create zip from these. Again, no writing on disk. I managed to get it to work. Almost - the code produces corrupted file.
Fun fact is that commented part produces working file. The difference is in 11th and 12th byte. It seems to be checksum or file length, but in corrupted file it is different each time.
The solution to this problem is using DotNetZip lib, but I think it is a matter of one line or something, because there are only 2 bytes wrong. (Bytes 12-15 correspond to modification date and time, so they had to be different. I'm not sure why my editor didn't show me that there's chunk of data missing at the end).
Corrupted file is missing a signature at the end. It may be caused by wrong content length header or zip generation.
using (var memoryStream = new MemoryStream())
{
// var filepath = context.Server.MapPath("files\\") + Guid.NewGuid() + ".zip";
// var zip = ZipFile.Open(filepath, ZipArchiveMode.Create);
var mzip = new ZipArchive(memoryStream, ZipArchiveMode.Create);
while (reader.Read())
{
//zip.CreateEntryFromFile((string) reader["path"], (string) reader["name"], CompressionLevel.Optimal);
var contents = (byte[]) reader["contents"];
var stream = mzip.CreateEntry((string) reader["name"], CompressionLevel.Optimal).Open();
using (var fs = new MemoryStream(contents))
{
fs.CopyTo(stream);
}
stream.Close();
}
// zip.Dispose();
Response.Clear();
Response.ContentType = "application/ocetet-stream";
Response.HeaderEncoding = Response.ContentEncoding;
Response.AppendHeader("content-disposition", "attachment; filename=\"" + gname + ".zip\"");
//Response.AppendHeader("content-length", new FileInfo(filepath).Length.ToString());
Response.AppendHeader("content-length", memoryStream.Length.ToString());
// Response.TransmitFile(filepath);
Response.BinaryWrite(memoryStream.ToArray());
Response.Flush();
}
Also - I'm new to C#, so my code may be pretty bad.

Related

Writing binary data to ftp location

How come this code writes an empty file at given location?
No error messages.
// upload file
WebRequest upload = WebRequest.Create(ftp + path + "/" + file);
upload.Method = WebRequestMethods.Ftp.UploadFile;
upload.Credentials = new NetworkCredential(username, password);
String filePath = HttpContext.Current.Server.MapPath("~/temp/" + file); // path to file to upload
Stream myReadStream = new FileStream(filePath, FileMode.Create); // stream that can read binary data
BinaryWriter myStreamWriter = new BinaryWriter(upload.GetRequestStream()); // writer that can write the binary data to the FTP server
while (myReadStream.ReadByte() != -1)
{
myStreamWriter.Write(myReadStream.ReadByte());
}
myStreamWriter.Close();
myReadStream.Close();
Removing the while loop creates a file 4byte big and corrupt so I guess I cant get in the while loop like this.
You should call upload.GetResponse() after myStreamWriter closed.
PS: In the while you write ONE time for every TWO times read, is it really you want?

How to Download File

I have been following these links all listed below, i found the best way to write this SMALL create Excel and Download function. ( Using EPPlus for Excel )
Download file of any type in Asp.Net MVC using FileResult? + How to convert an Stream into a byte[] in C#?
Using a FileStreamResult with a MemoryStream in ASP.NET MVC 3
Writing A Custom File Download Action Result For ASP.NET MVC
It runs through the code perfectly without error every time I run this but does not "Kick out" the file to be downloaded ( in a save as dialogue or w/e ).
public ActionResult ShowReport()
{
using (var stream = new MemoryStream())
{
ExcelPackage pck = new ExcelPackage();
var ws = pck.Workbook.Worksheets.Add("Sample1");
ws.Cells["A1"].Value = "Sample 1";
ws.Cells["A1"].Style.Font.Bold = true;
var shape = ws.Drawings.AddShape("Shape1", eShapeStyle.Rect);
shape.SetPosition(50, 200);
shape.SetSize(200, 100);
shape.Text = "Sample 1 text text text";
var fileDownloadName = "sample.xlsx";
var contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";//System.Net.Mime.MediaTypeNames.Application.Octet
var fileStream = new MemoryStream();
pck.SaveAs(fileStream);
fileStream.Position = 0;
var fsr = new FileStreamResult(fileStream, contentType);
fsr.FileDownloadName = fileDownloadName;
byte[] fileBytes = ReadToEnd(fileStream);
string fileName = "example";
return File(fileBytes, contentType, fileName);
}
}
What am I doing wrong / missing? - Must i write that Dialogue myself?
PN: I have also attempted this way
byte[] fileBytes = ReadToEnd(fileStream);
string fileName = "example";
return File(fileBytes, contentType, fileName);
ofcourse i had to figure out how to convert Stream to Byte but it also did not show anything.
Image of Chrome's Network Development Tool
Sorry about the small image ( if you can't see it scroll in with ctl+MouseWheel ) if your in a supporting browswer.
(In response to the comment thread above.)
From the image posted it looks like the actual file request (the last one in the list) is coming from JavaScript code instead of from a normal document-level request. Given this, it's highly likely that the server-side code is working correctly and returning the correct response.
However, since it's an AJAX request, the browser doesn't actually know what to do with the response. There are some potential solutions here. Ideally, you'll want to make this a normal request and remove AJAX from the picture if possible. If that's not an option, you can still initiate a document-level request from JavaScript. Something as simple as this:
window.location = '#Url.Action("Method", "Controller")';
This would be initiated from JavaScript code as it currently is, but would be for the whole browser instead of an AJAX request. That should do the trick.
Using the memory stream you have you can simple pass that to the Response object once you have saved the Excel Package
Code:
Response.AddHeader("content-disposition", "attachment;filename=FILENAME.xlsx")
Response.Charset = String.Empty
Response.ContentType = "application/ms-excel"
Response.BinaryWrite(stream.ToArray())
Response.End()

How to open up a zip file from MemoryStream

I am using DotNetZip.
What I need to do is to open up a zip files with files from the server.
The user can then grab the files and store it locally on their machine.
What I did before was the following:
string path = "Q:\\ZipFiles\\zip" + npnum + ".zip";
zip.Save(path);
Process.Start(path);
Note that Q: is a drive on the server. With Process.Start, it simply open up the zip file so that the user can access all the files. I like to do the same but not store the file on disk but show it from memory.
Now, instead of storing the zip file on the server, I like to open it up with MemoryStream
I have the following but does not seem to work
var ms = new MemoryStream();
zip.Save(ms);
but not sure how to proceed further in terms of opening up the zip file from a memory stream so that the user can access all the files
Here is a live piece of code (copied verbatim) which I wrote to download a series of blog posts as a zipped csv file. It's live and it works.
public ActionResult L2CSV()
{
var posts = _dataItemService.SelectStuff();
string csv = CSV.IEnumerableToCSV(posts);
// These first two lines simply get our required data as a long csv string
var fileData = Zip.CreateZip("LogPosts.csv", System.Text.Encoding.UTF8.GetBytes(csv));
var cd = new System.Net.Mime.ContentDisposition
{
FileName = "LogPosts.zip",
// always prompt the user for downloading, set to true if you want
// the browser to try to show the file inline
Inline = false,
};
Response.AppendHeader("Content-Disposition", cd.ToString());
return File(fileData, "application/octet-stream");
}
You can use:
zip.Save(ms);
// Set read point to beginning of stream
ms.Position = 0;
ZipFile newZip = ZipFile.Read(ms);
See the documentation for Create a zip using content obtained from a stream.
using (ZipFile zip = new ZipFile())
{
ZipEntry e= zip.AddEntry("Content-From-Stream.bin", "basedirectory", StreamToRead);
e.Comment = "The content for entry in the zip file was obtained from a stream";
zip.AddFile("Readme.txt");
zip.Save(zipFileToCreate);
}
After saving it, you can then open it up as normal.

save the document created by docX into response and send it to user for downloading

I am trying to use the amazing DocX library on codeplex to create a word document.
when the user clicks a button, the document is created and I want to be able to send it to the user immediately via response.. I am doing something similar to this now:
Edited code based on suggestions
using (DocX Report = DocX.Create(string.Format("Report-{0}.doc", DateTime.Now.Ticks)))
{
Paragraph p = Report.InsertParagraph();
p.Append("Title").FontSize(30).Bold()
.Append("Sub title").FontSize(28)
.AppendLine()
.Append(DateTime.Now.Date)
;
MemoryStream ms = new MemoryStream();
Report.SaveAs(ms);
Response.Clear();
Response.AddHeader("content-disposition", "attachment; filename=\"" + fileName + ".docx\"");
Response.ContentType = "application/msword";
Response.Write(ms);
Response.End();
}
I have tried a few variations of this.. but I am not able to achieve what I want.. Looking at this answer I can possibly save the document on the server and open with io stream.. but I want to avoid that extra step (and then I need to delete the file too)
I don't see the point of creating a file for few milli seconds.. there has to be a way to save the contents and send them to response stream.. right?
How'd I go about it?
thanks..
EDIT: my current code either throws up cannot open file (Access denied) error If I am using file stream, OR downloads an empty document file without any content (sometimes, type of response is written to document)
This code gets me an MS word document with System.IO.MemoryStream as it's content..
Okay, here is the final working solution:
For some reason, DocX library doesn't want to save to Response.OutputStream directly, so I had to save it to memory stream and write the memory stream to response, like Neil & Daniel suggested. Here's what worked for me:
MemoryStream ms = new MemoryStream()
Report.SaveAs(ms);
Response.Clear();
Response.AddHeader("content-disposition", "attachment; filename=\"" + fileName + ".doc\");
Response.ContentType = "application/msword";
ms.WriteTo(Response.OutputStream);
Response.End();
This might be a bit late, but I found a way to get this working with FileStreamResult:
public FileStreamResult DownloadDocument()
{
using (DocX document = DocX.Create(#"Test.docx"))
{
// Insert a new Paragraphs.
Paragraph p = document.InsertParagraph();
p.Append("I am ").Append("bold").Bold()
.Append(" and I am ")
.Append("italic").Italic().Append(".")
.AppendLine("I am ")
.Append("Arial Black")
.Font(new FontFamily("Arial Black"))
.Append(" and I am not.")
.AppendLine("I am ")
.Append("BLUE").Color(Color.Blue)
.Append(" and I am")
.Append("Red").Color(Color.Red).Append(".");
var ms = new MemoryStream();
document.SaveAs(ms);
ms.Position = 0;
var file = new FileStreamResult(ms, "application/vnd.openxmlformats-officedocument.wordprocessingml.document")
{
FileDownloadName = string.Format("test_{0}.docx", DateTime.Now.ToString("ddMMyyyyHHmmss"))
};
return file;
}
}
The important bit is setting the Position of the memorystream back to 0, otherwise it appeared to be at the end, and the file was returning empty.
Try using a MemoryStream instead of a FileStream.
Your current code looks really wrong:
You are saving the report to the OutputStream of the current response and then clear that response (!)
When you do Report.SaveAs(response.OutputStream); - it already writes file contents to the output stream. You don't need to do Response.Write(response.OutputStream);
So you code should look like this:
...
Report.SaveAs(response.OutputStream);
Response.AddHeader("content-disposition", "attachment; filename=\"" + fileName + ".doc\"");
Response.ContentType = "application/msword";
I think you've got things a little back to front and confused.
First off, clear the output, then add the headers, then write out the content.
Response.Clear();
Response.AddHeader("content-disposition", "attachment; filename=\"" + fileName + ".doc\"");
Response.ContentType = "application/msword";
// This writes the document to the output stream.
Report.SaveAs(response.OutputStream);
Response.End();
Also , if your file is a docx format file, append .docx rather than .doc to your filename.

Creating an Epub file with a Zip library

HI All,
I am trying to zip up an Epub file i have made using c#
Things I have tried
Dot Net Zip http://dotnetzip.codeplex.com/
- DotNetZip works but epubcheck fails the resulting file (**see edit below)
ZipStorer zipstorer.codeplex.com
- creates an epub file that passes validation but the file won't open in Adobe Digital Editions
7 zip
- I have not tried this using c# but when i zip the file using there interface it tells me that the mimetype file name has a length of 9 and it should be 8
In all cases the mimetype file is the first file added to the archive and is not compressed
The Epub validator that I'am using is epubcheck http://code.google.com/p/epubcheck/
if anyone has succesfully zipped an epub file with one of these libraries please let me know how or if anyone has zipped an epub file successfully with any other open source zipping api that would also work.
EDIT
DotNetZip works, see accepted answer below.
If you need to control the order of the entries in the ZIP file, you can use DotNetZip and the ZipOutputStream.
You said you tried DotNetZip and it (the epub validator) gave you an error complaining about the mime type thing. This is probably because you used the ZipFile type within DotNetZip. If you use ZipOutputStream, you can control the ordering of the zip entries, which is apparently important for epub (I don't know the format, just surmising).
EDIT
I just checked, and the epub page on Wikipedia describes how you need to format the .epub file. It says that the mimetype file must contain specific text, must be uncompressed and unencrypted, and must appear as the first file in the ZIP archive.
Using ZipOutputStream, you would do this by setting CompressionLevel = None on that particular ZipEntry - that value is not the default.
Here's some sample code:
private void Zipup()
{
string _outputFileName = "Fargle.epub";
using (FileStream fs = File.Open(_outputFileName, FileMode.Create, FileAccess.ReadWrite ))
{
using (var output= new ZipOutputStream(fs))
{
var e = output.PutNextEntry("mimetype");
e.CompressionLevel = CompressionLevel.None;
byte[] buffer= System.Text.Encoding.ASCII.GetBytes("application/epub+zip");
output.Write(buffer,0,buffer.Length);
output.PutNextEntry("META-INF/container.xml");
WriteExistingFile(output, "META-INF/container.xml");
output.PutNextEntry("OPS/"); // another directory
output.PutNextEntry("OPS/whatever.xhtml");
WriteExistingFile(output, "OPS/whatever.xhtml");
// ...
}
}
}
private void WriteExistingFile(Stream output, string filename)
{
using (FileStream fs = File.Open(fileName, FileMode.Read))
{
int n = -1;
byte[] buffer = new byte[2048];
while ((n = fs.Read(buffer,0,buffer.Length)) > 0)
{
output.Write(buffer,0,n);
}
}
}
See the documentation for ZipOutputStream here.
Why not make life easier?
private void IonicZip()
{
string sourcePath = "C:\\pulications\\";
string fileName = "filename.epub";
// Creating ZIP file and writing mimetype
using (ZipOutputStream zs = new ZipOutputStream(sourcePath + fileName))
{
var o = zs.PutNextEntry("mimetype");
o.CompressionLevel = CompressionLevel.None;
byte[] mimetype = System.Text.Encoding.ASCII.GetBytes("application/epub+zip");
zs.Write(mimetype, 0, mimetype.Length);
}
// Adding META-INF and OEPBS folders including files
using (ZipFile zip = new ZipFile(sourcePath + fileName))
{
zip.AddDirectory(sourcePath + "META-INF", "META-INF");
zip.AddDirectory(sourcePath + "OEBPS", "OEBPS");
zip.Save();
}
}
For anyone like me who's searching for other ways to do this, I would like to add that the ZipStorer class from Jaime Olivares is a great alternative. You can copy the code right into your project, and it's very easy to choose between 'deflate' and 'store'.
https://github.com/jaime-olivares/zipstorer
Here's my code for creating an EPUB:
Dictionary<string, string> FilesToZip = new Dictionary<string, string>()
{
{ ConfigPath + #"mimetype", #"mimetype"},
{ ConfigPath + #"container.xml", #"META-INF/container.xml" },
{ OutputFolder + Name.Output_OPF_Name, #"OEBPS/" + Name.Output_OPF_Name},
{ OutputFolder + Name.Output_XHTML_Name, #"OEBPS/" + Name.Output_XHTML_Name},
{ ConfigPath + #"style.css", #"OEBPS/style.css"},
{ OutputFolder + Name.Output_NCX_Name, #"OEBPS/" + Name.Output_NCX_Name}
};
using (ZipStorer EPUB = ZipStorer.Create(OutputFolder + "book.epub", ""))
{
bool First = true;
foreach (KeyValuePair<string, string> File in FilesToZip)
{
if (First) { EPUB.AddFile(ZipStorer.Compression.Store, File.Key, File.Value, ""); First = false; }
else EPUB.AddFile(ZipStorer.Compression.Deflate, File.Key, File.Value, "");
}
}
This code creates a perfectly valid EPUB file. However, if you don't need to worry about validation, it seems most eReaders will accept an EPUB with a 'deflate' mimetype. So my previous code using .NET's ZipArchive produced EPUBs that worked in Adobe Digital Editions and a PocketBook.

Categories

Resources