1

I have a URL to a file, "http://mydomain.domain.com/files/somefile.mp3".

I also have an Action in my Controller.

I want the action to return the file as an attachment.

I know I can do this:

Response.AddHeader("Content-Disposition", "attachment; filename=" + fileName)
Response.AddHeader("Content-Length", lenOfFile)
Response.ContentType = "application/octet-stream" 

But does that mean that the return type of the Action is void? And then what do I call to tell it to send?

I also tried:

return File(new FileStream(a.AssetPath, FileMode.Open), "application/octet-stream");

when the return type of the Action was FileStreamResult but it did not like the fact that my path was a URL.

MetaGuru
  • 42,847
  • 67
  • 188
  • 294
  • 1
    It's worth noting that your Controller Action can return a file as attachment with one of the File overloads: `return File(fileStream, "application/pdf", "suggestedName.pdf");` – James McCormack Mar 07 '12 at 12:16

3 Answers3

2

Maybe with one of the sub classes of FileResult -> http://msdn.microsoft.com/en-us/library/system.web.mvc.fileresult.aspx

If non of the built-in work, you can write your own actionresult, with just the code you posted.

ps. random ActionResult example from a quick search

eglasius
  • 35,831
  • 5
  • 65
  • 110
  • 2
    For his specific case, he could do the following: return new FilePathResult(a.AssetPath, "application/octet-stream") { FileDownloadName = fileName }; – Jim Geurts Apr 14 '11 at 17:27
1

I would suggest writing your own ActionResult, like this:

public class FileUriResult : ActionResult
{
  private string _contentType;
  private string _fileUri;
  private long _fileLength;

  public FileUriResult(string contentType, string fileUri, long fileLength)
  {
    _contentType = contentType;
    _fileUri = fileUri;
    _fileLength = fileLength;
  }

  public override void ExecuteResult(ControllerContext context)
  {
    if (context == null)
    {
      throw new ArgumentNullException("context");
    }
    HttpResponseBase response = context.HttpContext.Response;
    response.ContentType = _contentType;
    response.AddHeader("Content-Disposition", "attachment; filename=" + _fileUri);
    response.AddHeader("Content-Length", _fileLength.ToString(); 
  }
}

Then just use it in your action method:

return new FileUriResult("application/octet-stream", a.AssetPath, lenOfFile);                    
tpeczek
  • 23,867
  • 3
  • 74
  • 77
  • How can I get lenOfFile? Something like this maybe? http://stackoverflow.com/questions/122853/c-get-http-file-size ... and if the length of the file is unavailable can anything be done? – MetaGuru Sep 13 '10 at 16:18
  • Content-Length header is not obligatory, please read the following links: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.13, http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.4 – tpeczek Sep 13 '10 at 18:05
  • I am getting an error with this code, instead of downloading the file that exists at the path it is sending me 0kb files that simply have the same name as the real file, sample path would be like "http://domain.com/folder/file.mp3"... any idea? – MetaGuru Sep 13 '10 at 18:22
  • Well yes. I trusted in what you write and haven't tested it. But the headers themself will not make any result, you have to put file content into response. Stupid of me... – tpeczek Sep 13 '10 at 20:02
  • After some thinking, I need to ask you few question. Do you really have to send file from other server than your applicaton is using (because if you are sending local file, you can use local path with one of standard results). If you have to send file from other server, than why can't you just make a direct link in html? If none of the above is an option, I will prepare you a code which downloads a file from remote server and sends it to client, but thats not efficient solution. – tpeczek Sep 13 '10 at 20:16
  • Yeah you are right that does seem slower, the whole point of having the files on the other server is that it is a CDN host so it's suppose to be really fast globally, if I am downloading it to my web server and handing it back it defeats the whole purpose, I need some way though to tell the file, no matter what the type is, to download as a file attachment and not play it in the browser, I can't think of any other way than this but you're right, it defeats the purpose of itself. – MetaGuru Sep 14 '10 at 17:43
  • If despite this, you want me to edit the response and make it working code (with download to the server), let me know. – tpeczek Sep 14 '10 at 17:52
0

What you needed was to use a WebRequest to get the file and then use FileResult to give it back to the client.

public static FileResult FileWebRequest(this Controller controller, Uri uri, 
  string contentType) 
{
  return controller.FileWebResult(uri, contentType, null);
}

public static FileResult FileWebResult(this Controller controller, Uri uri, 
  string contentType, string fileDownloadName) 
{
  WebRequest request = WebRequest.Create(uri);
  using (WebResponse response = request.GetResponse()) 
  {
    byte[] buffer = new byte[response.ContentLength];
    using (Stream responseStream = response.GetResponseStream())
    using (MemoryStream memStream = new MemoryStream()) 
    {
      int bytesRead;
      int totalBytesRead = 0;
      while ((bytesRead = responseStream.Read(buffer, 0, buffer.Length)) > 0) 
      {
        memStream.Write(buffer, totalBytesRead, bytesRead);
      }
      FileResult result = new FileContentResult(memStream.ToArray(), contentType);
      if (fileDownloadName != null) {
        result.FileDownloadName = fileDownloadName;
      }
      return result;
    }
  }
}

Then to use you just call like this from your controller:

return this.FileWebResult(new Uri("http://mydomain.domain.com/files/somefile.mp3"), 
  "application/octet-stream", fileName);
Gordon Tucker
  • 6,653
  • 3
  • 27
  • 41
  • A note for any future viewers: I think I may have misunderstood the question. I though the question was asking how to download the file on the server and stream the results back to the user. – Gordon Tucker Sep 29 '11 at 19:43
  • OT: you cannot invoke `FileWebResult` by referencing `this` instance: the method is `static`. – T-moty Jan 19 '15 at 10:21
  • @T-moty - Sure you can. The static method is an extension, which makes all `Controller` classes have a 'local' method called `FileWebRequest` – Gordon Tucker Jan 26 '15 at 22:46
  • my bad: I had not seen the 'this' keyword near the first parameter :) – T-moty Jan 27 '15 at 08:31