We have an ASP.NET application which dynamically generates Excel documents and streams them to the client, using Response.WriteFile, see code below. Note that the document is deleted once the file has been written to the client. Thus, no documents are ever left on the server.
However, my client's users has now all upgraded to Office 2010, and now the documents will open in "Protected View". In order to edit the document, the user has to click "Enable editing" first. This is considered unacceptable for the users.
The reason that this happens is that streamed documents are placed in the Temporary Internet files, and this is considered a "potentially unsafe location". And documents in such locations are opened in protected view. I am just hoping there is some way to work around this.
Theoretically, I could place the document in a folder which is accessible from the client, and redirect to the document.
This solution is not an option, however. Firstly, since the document would be left on the server, it could be accessible for other users, which is a problem since the documents may contain confidential data.
There are other reasons why this is not a vialable option.
An other theoretical workaround would be to ask all users to disable the setting "Enable protected view for files located in potentially unsafe locations". Naturally, this is not an option either.
So, in short, is there anyway to avoid the documents to be opened in "Protected view" while using the streaming technique described below?
Response.Buffer = true;
Response.Clear();
Response.AddHeader("Pragma", "no-cache");
Response.Expires = 0;
Response.AddHeader("Content-Type", contentType);
Response.AddHeader("Content-Disposition", "attachment; filename=" + proposedFilename);
Response.WriteFile(dstFullPathName);
Response.Flush();
Response.Close();
File.Delete(dstFullPathName);
HttpContext.Current.ApplicationInstance.CompleteRequest();
Related
I am trying to download a file from an url using WebClient and multi thread. I am trying to get ProgressPercentage while downloading file and I am trying to do that with DownloadProgressChanged but when I try to print percentage to with debug.WriteLine everything is okey. But when I want to change my textbox.text its not working. I want to update my textbox while downloading.
With this one I am calling a function.
wc.DownloadProgressChanged += new DownloadProgressChangedEventHandler(DownloadProgressChangedEventHandler);
This is the content of function:
public void DownloadProgressChangedEventHandler(object sender, DownloadProgressChangedEventArgs e)
{
//Debug.WriteLine works normal.
Debug.WriteLine(e.ProgressPercentage);
TextBox1.Text = "Progress:" +e.ProgressPercentage.ToString();
progressBarDownload3.Style.Add("width",e.ProgressPercentage.ToString()+"%");
}
Technically, While the solution above could be useful if you are working on a desktop or mobile application but if you are using ASP.NET Server-Side Component and your server is downloading something for you while the page is being posted back, then there is no way the front end can be updated before the download is completed and response is sent back to the requesting client. This is the whole sort of point, every request that goes to the server will only return once the response is completed/interrupted, it cannot come back just to report the progress and then go back to the point where it was running.
If you want to display the progress of the file that is being downloaded then you should go for a client side (JavaScript/jQuery) based solutions.
If you strictly want to download the file with C# code then try checking out for SignalR library, it could be helpful for you in reporting the changes in the backend to the frontend but at the same time, it would be complicated to manage just to report download progress.
Alternatively, you can throw the browser to the download url and it would download the file automatically for the client and the browser's default download progress bar would be visible to the clients downloading the file.
No matter what, you cannot download files from the server and report each byte that gets downloaded to the front end using C# and ASP.NET WebForms/MVC. Unless you include jquery/javascript in the action.
I have a Filetable containing many different document types (.doc;.pdf;.xls etc).
I am writing a small web (C# / .net 4) search app. Search works great using fulltext index with filetable to find content.
But I'm struggling to find a way in my app to have the search results as links which can launch the document in question? And just handle the different file types? (Assume client has Word/adobe/excel installed etc)
Grateful for any advice.
You will need to write a custom page handler to stream the bytes to the client with the proper HTTP headers. You will need to decide whether to support inline viewing (open in the browser - Content-Disposition: inline) versus external viewing using an attachment (e.g. Content-Disposition: attachment).
Response.AddHeader("Content-Disposition", "attachment; filename=example.pdf");
If you are using ASP.NET MVC - you can leverage the FileResult to streamline this process, but creating your own handler wouldn't be too much different.
public FileResult Download()
{
byte[] fileBytes = ...; // from FileTable
string fileName = "example.txt";
return File(fileBytes, System.Net.Mime.MediaTypeNames.Application.Octet, fileName);
}
The best approach to handling various MIME types (PDF, DOC, XLS) is to statically define the supported file types or dynamically read IIS and assign the appropriate Content-Type HTTP header.
Response.ContentType = "application/pdf";
I've got a page that allows users to enter search criteria and then display matching records. It also has a download button to enable the user to download matching records.
How can I code it so that clicking on "Download" will first refresh the record display before downloading the data?
This is the code that I'm using for the download:
Response.ClearContent();
Response.ClearHeaders();
using (MemoryStream outputStream = new MemoryStream())
{
// some details elided...
outputStream.Write(documentData, 0, documentData.Count());
string fileName = GenerateFileName();
Response.AppendHeader("content-disposition", String.Format("attachment; filename={0}", fileName));
outputStream.Flush();
outputStream.WriteTo(Response.OutputStream);
}
Response.Flush();
Response.Close();
Only one response you can send back to the browser, ether you update the data, ether you send the new header to start the download.
To make both of them you need to change your steps probably using some javascript and/or ajax call.
How HTTP protocol works: http://www.w3.org/Protocols/rfc2616/rfc2616-sec1.html
Construct a javascript method that first updates the page via AJAX, then proceeds to make a non-AJAX request to download the file. As Aristos says, this cannot be done in a single request. A different solution could be to download the file first (non-ajax), then refresh the page without ajax. Normally, javascript code cannot be executed correctly after a new non-ajax request is made, but if it only downloads a file, I think the code might continue its execution to post the next request.
I'm building a site in which users can purchase MP3 files which they can download from their user login area.
In previous applications I've developed, I would allow admin to upload the file and it would be stored under "/Uploads/MP3s/filename.mp3". However, I need to make this secure so that users cannot gain access to these files until they have purchased them.
What is the best, and most secure, way of doing this?
You should have a database where you store which user bought which mp3. Uploaded mp3's should not be stored in an openly accessable folder. Store them in another folder then the httpfolder, but make sure your iis has access to this folder. This way nobody can guess the path to the file because it's not in under the http-root.
Use a download page which checks the download permissions and only then sends the mp3 to the user with Response.WriteFile(filename) and the correct mime-type etc.
Protected Sub ServeMP3(ByVal f As FileInfo)
Response.Clear()
Response.ContentType = "audio/mpeg3"
Response.AddHeader("content-disposition", "inline; filename=" & f.Name)
Response.WriteFile(f.FullName)
Response.End()
End Sub
Instead of "inline" (stream and play), you can use "attachment" to force a file download
Hide them behind a HTTP Handler, Module, Web Service or Page that can check the validity of the request, and then stream the file or display an error/ redirect to the purchase page.
This will have the advantage of completely abstracting away the real paths for the files too...security through obscurity (:
I have an ASP.net application which returns a binary PDF file (stored from the database, which was previously uploaded) to the client.
The code I have works fine for all browsers, except for internet explorer 6 (story of my life!). In IE6, when a user clicks open, Adobe reports the error: "There was an error opening this document. The file cannot be found."
When the user clicks save, the PDF saves fine and can be opened by double clicking it. I am stumped. Google has given suggestions of caching (setting cachecontrol to private etc) and I have tried all of those but nothing has worked.
The wierd(er) behaviour is when I generate a PDF in my ASP .net server layer (using NFop) from scratch, IE will open it fine (using the SAME code!).
Here is my code for sending the binary data across the wire:
// Firefox doesn't like spaces in filenames.
filename = filename.Replace(" ", "_");
string extension = Path.GetExtension(filename);
string contentType;
switch (extension)
{
case "pdf":
contentType = "application/pdf";
break;
default:
contentType = "application/x-unknown";
break;
}
context.Response.Clear();
context.Response.AddHeader("content-disposition", "attachment;filename=" + filename);
context.Response.Charset = "";
context.Response.ContentType = contentType;
context.Response.BinaryWrite(data);
context.Response.Flush();
Here are application versions:
ASP .net 3.5
IE6.0.2900.2180.xpsp_s
List item
p2_gdr.091208-2028
Adobe Reader version 8.0.0
Windows XP SP 2
SQL Server 2008
Any help/suggestions would be much appreciated. Thanks :)
EDIT:
I have plugged in Fiddler to view the headers and sure enough it does appear to be a caching issue. grrr!
When my NFop PDF (the one that works) is uploaded, it is sending cache-control = private.
When my attachment PDF (the one that doesn't work) is uploaded, it sends no-cache.
I have looked in the Response object and both appear to have the same headers when I call context.Response.Flush().
Still stumped!
SOLVED:
Somewhere in our framework was a rogue method which was being invoked using reflection:
///
/// Sets the expiratoin of the request and force no cache
///
protected void SetCacheExpiration(HttpContext context)
{
//sets the cache to expire immediately
context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
context.Response.Cache.SetSlidingExpiration(true);
context.Response.Cache.SetExpires(DateTime.Now);
context.Response.Cache.SetMaxAge(new TimeSpan(0, 0, 0));
context.Response.Cache.SetNoStore();
context.Response.Cache.SetAllowResponseInBrowserHistory(false);
context.Response.Cache.SetValidUntilExpires(false);
context.Response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
}
Thanks for your help, caching! It is interesting that the only browser that actually didn't cache the download (when opened) was IE6.
Here the method I've used before to render in-browser PDFs for IE6. Again... this was for a project where we showed the PDF in the browser, but it should serve you well for a PDF that's shown in the reader.
Somewhere in our framework was a rogue method which was being invoked using reflection:
/// <summary>
/// Sets the expiratoin of the request and force no cache
/// </summary>
protected void SetCacheExpiration(HttpContext context)
{
//sets the cache to expire immediately
context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
context.Response.Cache.SetSlidingExpiration(true);
context.Response.Cache.SetExpires(DateTime.Now);
context.Response.Cache.SetMaxAge(new TimeSpan(0, 0, 0));
context.Response.Cache.SetNoStore();
context.Response.Cache.SetAllowResponseInBrowserHistory(false);
context.Response.Cache.SetValidUntilExpires(false);
context.Response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
}
Thanks for your help, caching! It is interesting that the only browser that actually didn't cache the download (when opened) was IE6.