15

I am streaming data from server to client for download using filestream.write. In that case what is happening is that I am able to download the file but it does not appear as download in my browser. Neither the pop-up for "Save As" appears not "Download Bar" appears in Downloads section. From looking around, I guess I need to include "something" in the response header to tell the browser that there is an attachment with this response. Also I want to set the cookie. To accomplish this, this is what I am doing:

        [HttpContext.Current.Response.AppendHeader("Content-Disposition","attachment;filename=" & name)]
    public ActionResult Download(string name)
    {
          // some more code to get data in inputstream.

          using (FileStream fs = System.IO.File.OpenWrite(TargetFile))
            {
                byte[] buffer = new byte[SegmentSize];
                int bytesRead;
                while ((bytesRead = inputStream.Read(buffer, 0, SegmentSize)) > 0)
                {
                    fs.WriteAsync(buffer, 0, bytesRead);
                }
            }
        }
        return RedirectToAction("Index");
    }

I am getting error that: "System.web.httpcontext.current is a property and is used as a type."

Am I doing the header updating at the right place? Is there any other way to do this?

tereško
  • 58,060
  • 25
  • 98
  • 150
ezile
  • 571
  • 2
  • 6
  • 20

5 Answers5

17

Yes, You are doing it the wrong way try this, you should add the header inside your action not as an attribute header to your method.

HttpContext.Current.Response.AppendHeader("Content-Disposition","attachment;filename=" & name)

or

Request.RequestContext.HttpContext.Response.AddHeader("Content-Disposition", "Attachment;filename=" & name)

Update As i understand you are making an ajax call to your controller/action which wont work for file download by directly calling an action. You can achieve it this way.

public void Download(string name)
        {
//your logic. Sample code follows. You need to write your stream to the response.

            var filestream = System.IO.File.ReadAllBytes(@"path/sourcefilename.pdf");
            var stream = new MemoryStream(filestream);
            stream.WriteTo(Response.OutputStream);
            Response.AddHeader("Content-Disposition", "Attachment;filename=targetFileName.pdf");
            Response.ContentType = "application/pdf";
        }

or

    public FileStreamResult Download(string name)
    {
        var filestream = System.IO.File.ReadAllBytes(@"path/sourcefilename.pdf");
        var stream = new MemoryStream(filestream);


        return new FileStreamResult(stream, "application/pdf")
        {
            FileDownloadName = "targetfilename.pdf"
        };
    }

In your JS button click you can just do something similar to this.

 $('#btnDownload').click(function () {
            window.location.href = "controller/download?name=yourargument";
    });
PSL
  • 123,204
  • 21
  • 253
  • 243
  • I tried doing this. But it still neither show the "Save As" dialog box and nor the download progress bar in downloads section of browser. – ezile Apr 19 '13 at 16:56
  • This works for me and i see the view html getting downloaded in my test case. For you it could be because you are Redirecting to Action. I haven't tried this scenario though. Just try placing this header under Index just for a try. – PSL Apr 19 '13 at 18:32
  • I will detail a bit more. I am at "Index" page and then clicks on Download which then call Download action through JavaScript. The above code is written in Download action. And after streaming the file to user, I redirect to Index page. I am confused as in when should I tell browser that there is a download. I mean I am sending the stuff using FileStream.WriteSync, so I guess I need to tell browser before sending the file that there is an attachment and thus it will pop-up the "save as" dialog box. I think I am confused in the workflow here and thus don't when and where should I make change. – ezile Apr 19 '13 at 22:28
  • Ok got your point.. SO you want to initite download and no matter what you want to redirect to your new page right. – PSL Apr 19 '13 at 23:34
  • You said you are directing to action through javascript, are you using xhr request, ajax request ot just location.href? – PSL Apr 19 '13 at 23:59
  • I am using ajax request. Well, its not really necessary to redirect. I tried without redirecting too but it was not working. And I just checked, the headers are there in the response header. But still it does not show up as download. – ezile Apr 20 '13 at 00:16
  • Please see my edit.. This should work for you Ajax calls are not intended for file downloads. Also i just found this see if this helps too http://stackoverflow.com/questions/6668776/download-file-through-an-ajax-call-php – PSL Apr 20 '13 at 02:39
  • Hey, thanks a lot. But I am reading from a URL, and File.ReadAllBytes takes a file. The download is happening like this. There are two servers. One is data server and another is application server. Browser requests the file from application server which then gets the file from data server. Application server has limitations on size. Thus it takes data from data server in chunks(from the stream) and then send the data to browser in chunks through the stream connected to browser(here, `FileStream`. In my code, inputstream is the stream b/w data and application server and FileStream (Contd..) – ezile Apr 21 '13 at 02:31
  • b/w application and browser. So, I cannot read all the bytes. This chunks thing is working the way I did but in that case it does show up as "download" to client even though it downloads it on local machine. – ezile Apr 21 '13 at 02:33
  • I just checked again. The Content-Length header is zero. Its not getting set. Can that be a reason for the whole prob? Also how can I set it? – ezile Apr 21 '13 at 04:25
  • Are you writing it to the Response Steam? – PSL Apr 21 '13 at 04:26
  • I didn't use your code since I cannot do `ReadAllBytes` thing. I am still using the code I mentioned in the question. Does that answer your question? – ezile Apr 21 '13 at 05:03
5

Please take a look here.

Following is taken from referenced website.

public FileStreamResult StreamFileFromDisk()
{
    string path = AppDomain.CurrentDomain.BaseDirectory + "uploads/";
    string fileName = "test.txt";
    return File(new FileStream(path + fileName, FileMode.Open), "text/plain", fileName);
}

Edit 1:

Adding something that might be more of your interest from our good ol' SO. You can check for complete detail here.

public ActionResult Download()
{
    var document = ...
    var cd = new System.Net.Mime.ContentDisposition
    {
        // for example foo.bak
        FileName = document.FileName, 

        // 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(document.Data, document.ContentType);
}
Community
  • 1
  • 1
Abhinav
  • 2,085
  • 1
  • 18
  • 31
0

Change:

return RedirectToAction("Index");

to:

return File(fs, "your/content-type", "filename");

And move the return statement to inside your using statement.

Mathijs Flietstra
  • 12,900
  • 3
  • 38
  • 67
0

In the past I built a whitelist to allow some domains to iframe my site. Remember Google's image cache used to iframe sites as well.

static HashSet<string> frameWhiteList = new HashSet<string> { "www.domain.com",
                                                    "mysub.domain.tld",
                                                    "partner.domain.tld" };

    protected void EnforceFrameSecurity()
    {
        var framer = Request.UrlReferrer;
        string frameOptionsValue = "SAMEORIGIN";

        if (framer != null)
        {
            if (frameWhiteList.Contains(framer.Host))
            {
                frameOptionsValue = string.Format("ALLOW-FROM {0}", framer.Host);
            }

        }

        if (string.IsNullOrEmpty(HttpContext.Current.Response.Headers["X-FRAME-OPTIONS"]))
        {
            HttpContext.Current.Response.AppendHeader("X-FRAME-OPTIONS", frameOptionsValue);
        }
    }
Wayne
  • 1
0
public FileResult DownloadDocument(string id)
        {
            if (!string.IsNullOrEmpty(id))
            {
                try
                {
                    var fileId = Guid.Parse(id);

                var myFile = AppModel.MyFiles.SingleOrDefault(x => x.Id == fileId);

                if (myFile != null)
                {
                    byte[] fileBytes = myFile.FileData;
                    return File(fileBytes, System.Net.Mime.MediaTypeNames.Application.Octet, myFile.FileName);
                }
            }
            catch
            {
            }
        }

        return null;
    }
ArK
  • 20,698
  • 67
  • 109
  • 136