I'm trying to create a ZIP file on the fly which might contain a few thousands of pictures.
public static void CompressAndSendFiles(List files)
{
HttpContext.Current.Response.ClearContent();
// LINE1: Add the file name and attachment, which will force the open/cance/save dialog to show, to the header
HttpContext.Current.Response.AddHeader("Content-Disposition", "attachment; filename=\"Jpeg Package "
+ DateTime.Now.ToString("MM-dd-yyyy hh-mm-ss") + ".zip\"");
// Add the file size into the response header
//HttpContext.Current.Response.AddHeader("Content-Length", file.Length.ToString());
// Set the ContentType
HttpContext.Current.Response.ContentType = ReturnHttpContentType("download.zip");
ZipOutputStream oZipStream =
new ZipOutputStream(HttpContext.Current.Response.OutputStream);
try
{
foreach (string file in files)
{
FileInfo fInfo = new FileInfo(file);
ZipEntry oZipEntry = new ZipEntry(fInfo.Name);
oZipStream.PutNextEntry(oZipEntry);
byte[] buffer = File.ReadAllBytes(file);
oZipStream.Write(buffer, 0, buffer.Length);
//oZipStream.Flush();
}
}
catch (Exception ex)
{
throw ex;
}
finally
{
oZipStream.Finish();
oZipStream.Close();
oZipStream.Dispose();
}
HttpContext.Current.Response.OutputStream.Flush();
HttpContext.Current.Response.End();
}
Everything is fine, unless when number of files get big.
My question:
Is there a way to initiate the download (let the download manager on client side popup), and then start writing on the stream?
I monitored w3wp.exe (IIS) process, and it seems that the data is being written on memory instead of Stream. When w3wp.exe memory usage riches a certain number, it releases the memory and nothing happens (no download).
Thank you in advance.
Have you tried using this?
HttpContext.Current.Response.BufferOutput = false;
As far as I know, you cannot stream it while archiving. You need to zip them first, then stream it. As the result, server is finally out of memory if the final zip is extremely large.
What I end up doing is I write it to a temporary zip file then stream that temporary file.
Related
I'm trying to create a scanning solution. Basically the user is physically scanning a page. The printer is making an API call, passing in the binary data of the scan in the body.
I'm trying to save this as a PDF on the server, but when I go to open the file, i'm getting an error "There is an error while reading a stream".
var bodyStream = new StreamReader(HttpContext.Current.Request.InputStream);
bodyStream.BaseStream.Seek(0, SeekOrigin.Begin);
var bodyText = bodyStream.ReadToEnd();
string pathToFiles = HttpContext.Current.Server.MapPath("~\\UploadedFiles\\WriteLines.pdf");
try
{
using (StreamWriter outputFile = new StreamWriter(pathToFiles, false))
{
outputFile.WriteLine(bodyText);
}
HttpContext.Current.Response.ContentType = "application/pdf";
}
catch (Exception ex)
{
throw (ex);
}
This is just testing something, and I have permissions etc for writing the file, it's just not creating a valid file.
Any thoughts on what I should use? I have looked into some libraries, but they don't seem to cover what i'm after
StreamReader.ReadToEnd convert bytes to string in particular encoding (UTF8 by default). I don't think this work for PDF.
You need copy bytes directly in the output file :
var bodyStream = HttpContext.Current.Request.InputStream;
bodyStream.Seek(0, SeekOrigin.Begin);
string pathToFiles = HttpContext.Current.Server.MapPath("~\\UploadedFiles\\WriteLines.pdf");
using (FileStream outputFile = File.Create(pathToFiles))
{
bodyStream.CopyTo(outputFile);
}
I've been having a bit of a problem lately. I've been trying to extract one zip file into a memory stream and then from that stream, use the updateEntry() method to add it to the destination zip file.
The problem is, when the file in the stream is being put into the destination zip, it works if the file is not already in the zip. If there is a file with the same name, it does not overwrite correctly. It says on the dotnetzip docs that this method will overwrite files that are present in the zip with the same name but it does not seem to work. It will write correctly but when I go to check the zip, the files that are supposed to be overwritten have a compressed byte size of 0 meaning something went wrong.
I'm attaching my code below to show you what I'm doing:
ZipFile zipnew = new ZipFile(forgeFile);
ZipFile zipold = new ZipFile(zFile);
using(zipnew) {
foreach(ZipEntry zenew in zipnew) {
percent = (current / zipnew.Count) * 100;
string flna = zenew.FileName;
var fstream = new MemoryStream();
zenew.Extract(fstream);
fstream.Seek(0, SeekOrigin.Begin);
using(zipold) {
var zn = zipold.UpdateEntry(flna, fstream);
zipold.Save();
fstream.Dispose();
}
current++;
}
zipnew.Dispose();
}
Although it might be a bit slow, I found a solution by manually deleting and adding in the file. I'll leave the code here in case anyone else comes across this problem.
ZipFile zipnew = new ZipFile(forgeFile);
ZipFile zipold = new ZipFile(zFile);
using(zipnew) {
// Loop through each entry in the zip file
foreach(ZipEntry zenew in zipnew) {
string flna = zenew.FileName;
// Create a new memory stream for extracted files
var ms = new MemoryStream();
// Extract entry into the memory stream
zenew.Extract(ms);
ms.Seek(0, SeekOrigin.Begin); // Rewind the memory stream
using(zipold) {
// Remove existing entry first
try {
zipold.RemoveEntry(flna);
zipold.Save();
}
catch (System.Exception ex) {} // Ignore if there is nothing found
// Add in the new entry
var zn = zipold.AddEntry(flna, ms);
zipold.Save(); // Save the zip file with the newly added file
ms.Dispose(); // Dispose of the stream so resources are released
}
}
zipnew.Dispose(); // Close the zip file
}
I'm getting a file from a database in byte [] format and want user to see download dialog before Linq will take it from the database. It's in C# and ASP.NET.
Now, it's like this:
User choose a file, click on it.
In code I get id of file clicked and using Linq I'm downloading.
Then I send the file by Response.OutputStream.Write(content, 0,
content.Length);
Before a file is downloaded from the database user won't see any
download dialog.
What can I do if I want users to see the download dialog before file is downloaded?
Code:
Getting file by id:
public static byte[] getFile(Guid id)
{
var linqFile = from file in MyDB.Files
where file.IdPliku.Equals(id)
select new
{
Content = file.Content
};
return linqFile.ToList().FirstOrDefault().Content.ToArray();
}
Saving file:
public void SaveFile(Guid fileID, string filename, string mimeTypes)
{
try
{
byte[] content = FileService.getFile(fileID);
Response.ClearContent();
Response.ClearHeaders();
Response.ContentType = mimeTypes;
Response.AppendHeader("Accept-Ranges", "bytes");
Response.AppendHeader("Content-Range", string.Format("0-{0}/{1}", content.Length, content.Length));
Response.AppendHeader("Content-Length", content.Length.ToString());
Response.AppendHeader("Content-Encoding", "utf-8");
Response.AppendHeader("Content-Type", Response.ContentType);
Response.AppendHeader("Content-Disposition", "attachment; filename= " + HttpUtility.UrlEncode(filename));
Response.OutputStream.Write(content, 0, content.Length);
//Response.BinaryWrite(content);
Response.Flush();
}
finally
{
Response.Close();
}
}
You are my hope.
your issue is here:
byte[] content = FileService.getFile(fileID);
because in this line you allocate the whole file in the web server's RAM and put everything in there, all content of the file from the database; what happens later does not matter anymore because you have already downloaded from db to web server in this line!!!
I am having such Deja-vu because I am sure I have given exactly the same comment on a very same question few weeks ago. Can't find it now, search for something like this here in SO.
In fact the solution is to stream directly to the output stream of the Response avoiding your byte[] array allocation above, to get this your data layer should of course support it and if it does not you could add a method for this. You want to use SQL Server filestream or something similar.
I have written a procedure that will open a xls from a local disc, refresh the data in it and then save it again. This works fine.
The problem occurs when I replace the filename to point to a SharePoint site. It opens the file fine. Refreshes the file, but when it trys to save the file it throws an exception with the message "Cannot save as that name. Document was opened as read-only.".
If I try and save the file with a different filename then it works fine.
Does anybody know what I am missing? I think it must have somethoing to do with how I am opening the file. Is there another way that I can force the opening of the file in a read/write manner?
private static void RefreshExcelDocument(string filename)
{
var xls = new Microsoft.Office.Interop.Excel.Application();
xls.Visible = true;
xls.DisplayAlerts = false;
var workbook = xls.Workbooks.Open(Filename: filename, IgnoreReadOnlyRecommended: true, ReadOnly: false);
try
{
// Refresh the data from data connections
workbook.RefreshAll();
// Wait for the refresh occurs - *wish there was a better way than this.
System.Threading.Thread.Sleep(5000);
// Save the workbook back again
workbook.SaveAs(Filename: filename); // This is when the Exception is thrown
// Close the workbook
workbook.Close(SaveChanges: false);
}
catch (Exception ex)
{
//Exception message is "Cannot save as that name. Document was opened as read-only."
}
finally
{
xls.Application.Quit();
xls = null;
}
}
Many thanks in advance for suggestions.
Jonathan
Unfortunately you can't save directly to SharePoint using the Excel API. That's why the file is being opened as read only - it's not allowed.
The good news is that it is possible, but you have to submit the form via a web request. Even better news is that there is sample code on MSDN! In particular notice the PublishWorkbook method that sends a local copy of the Excel file to the server via a web request:
static void PublishWorkbook(string LocalPath, string SharePointPath)
{
WebResponse response = null;
try
{
// Create a PUT Web request to upload the file.
WebRequest request = WebRequest.Create(SharePointPath);
request.Credentials = CredentialCache.DefaultCredentials;
request.Method = "PUT";
// Allocate a 1K buffer to transfer the file contents.
// The buffer size can be adjusted as needed depending on
// the number and size of files being uploaded.
byte[] buffer = new byte[1024];
// Write the contents of the local file to the
// request stream.
using (Stream stream = request.GetRequestStream())
using (FileStream fsWorkbook = File.Open(LocalPath,
FileMode.Open, FileAccess.Read))
{
int i = fsWorkbook.Read(buffer, 0, buffer.Length);
while (i > 0)
{
stream.Write(buffer, 0, i);
i = fsWorkbook.Read(buffer, 0, buffer.Length);
}
}
// Make the PUT request.
response = request.GetResponse();
}
finally
{
response.Close();
}
}
The sample code describes a scenario for the 2007 versions of these products but other versions should behave in the same way.
What does the filename of a failed example looks like? Aren't documents used in SharePoint stored in the database? Or am I getting your problem wrong? Otherwise I could imagine that the file you are trying to store is write protected by the operation system and cannot be modified.
I use the following code to send a .zip file to a customer, it works great for files around 60-80 Mb but when I try with larger files (300 Mb) customers report that the browser (both IE and FFX) just stops download progress after a few minutes (around (80Mb)) and nothing more happens.
I wonder if there could be a setup issue with IIS that stops the request handling thread to execute after a few minutes or if there is anything wrong with my code.
I could obviously find other ways to deliver .zip files but this issue is bothering me a bit and I want to get it working.
The code:
Response.ContentType = "application/zip";
Response.AddHeader("Content-Disposition", "attachment; filename=" + "hands.zip");
var fi = new FileInfo(dld.Path);
Response.AddHeader("Content-Length", fi.Length.ToString());
const int buffercnt = 50000;
var buffer = new byte[buffercnt];
using (var br = new BinaryReader( new StreamReader(dld.Path).BaseStream))
{
int read = br.Read(buffer, 0, buffercnt);
while(read != 0)
{
Response.OutputStream.Write(buffer, 0, read);
read = br.Read(buffer, 0, buffercnt);
Response.Flush();
}
}
Response.Close();
Response.End();
You can set the executionTimeout property in the httpRuntime tag in web.config to keep the code from being aborted.
Excuse me for saying it, but you have made a mess out of reading the file. You create a StreamReader only to have it open a FileStream for you, and you don't dispose of the StreamReader properly. Also, you use a BinaryReader, but you don't use any of the features of it, the Read method is available from the FileStream directly. So, skip the StreamReader and the BinaryReader and just create a FileStream yourself.