136

I am using ASP.NET Web API.
I want to download a PDF with C# from the API (that the API generates).

Can I just have the API return a byte[]? and for the C# application can I just do:

byte[] pdf = client.DownloadData("urlToAPI");? 

and

File.WriteAllBytes()?
1

8 Answers 8

200

Better to return HttpResponseMessage with StreamContent inside of it.

Here is example:

public HttpResponseMessage GetFile(string id)
{
    if (String.IsNullOrEmpty(id))
        return Request.CreateResponse(HttpStatusCode.BadRequest);

    string fileName;
    string localFilePath;
    int fileSize;

    localFilePath = getFileFromID(id, out fileName, out fileSize);
       
    HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
    response.Content = new StreamContent(new FileStream(localFilePath, FileMode.Open, FileAccess.Read));
    response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
    response.Content.Headers.ContentDisposition.FileName = fileName;
    response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");

    return response;
}

UPDATE from comment by patridge: Should anyone else get here looking to send out a response from a byte array instead of an actual file, you're going to want to use new ByteArrayContent(someData) instead of StreamContent (see here).

Sign up to request clarification or add additional context in comments.

10 Comments

First thing - this code will cause an exception since you're newing up two FileStream objects pointed at the same file. Second thing is that you do not want to use a "Using" statement, because as soon as the variable goes out of scope, .NET will dispose it and you'll get error messages about the underlying connection being closed.
Should anyone else get here looking to send out a response from a byte array instead of an actual file, you're going to want to use new ByteArrayContent(someData) instead of StreamContent (see here).
You may also want to override the base dispose() so you can handle your resources correctly when the framework calls it.
I would like to point out that the correct MediaTypeHeaderValue is crucial and to get it dynamic if you have different file types you can do like this. (where fileName is a string and has a file type ending like .jpg, .pdf, docx etc..) var contentType = MimeMapping.GetMimeMapping(fileName); response.Content.Headers.ContentType = new MediaTypeHeaderValue(contentType);
Does the FileStream get disposed automatically?
|
50

Just a note for .Net Core: We can use the FileContentResult and set the contentType to application/octet-stream if we want to send the raw bytes. Example:

[HttpGet("{id}")]
public IActionResult GetDocumentBytes(int id)
{
    byte[] byteArray = GetDocumentByteArray(id);
    return new FileContentResult(byteArray, "application/octet-stream");
}

1 Comment

This works great, Also if you wanna control the file name there is a property on FileContentResult called FileDownloadName to specify the filename
44

I made the follow action:

[HttpGet]
[Route("api/DownloadPdfFile/{id}")]
public HttpResponseMessage DownloadPdfFile(long id)
{
    HttpResponseMessage result = null;
    try
    {
        SQL.File file = db.Files.Where(b => b.ID == id).SingleOrDefault();

        if (file == null)
        {
            result = Request.CreateResponse(HttpStatusCode.Gone);
        }
        else
        {
            // sendo file to client
            byte[] bytes = Convert.FromBase64String(file.pdfBase64);


            result = Request.CreateResponse(HttpStatusCode.OK);
            result.Content = new ByteArrayContent(bytes);
            result.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
            result.Content.Headers.ContentDisposition.FileName = file.name + ".pdf";
        }

        return result;
    }
    catch (Exception ex)
    {
        return Request.CreateResponse(HttpStatusCode.Gone);
    }
}

3 Comments

This actually answers the question
This would not be a good idea with large files since it loads the entire image into memory. The stream option is better.
@PaulReedy Perfect... but in a lot of cases, you don't need to deal with large files. But I totally agree with your point!
22

Example with IHttpActionResult in ApiController.

[HttpGet]
[Route("file/{id}/")]
public IHttpActionResult GetFileForCustomer(int id)
{
    if (id == 0)
      return BadRequest();

    var file = GetFile(id);

    IHttpActionResult response;
    HttpResponseMessage responseMsg = new HttpResponseMessage(HttpStatusCode.OK);
    responseMsg.Content = new ByteArrayContent(file.SomeData);
    responseMsg.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
    responseMsg.Content.Headers.ContentDisposition.FileName = file.FileName;
    responseMsg.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
    response = ResponseMessage(responseMsg);
    return response;
}

If you don't want to download the PDF and use a browsers built in PDF viewer instead remove the following two lines:

responseMsg.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
responseMsg.Content.Headers.ContentDisposition.FileName = file.FileName;

2 Comments

@ElbertJohnFelipe Yes, you get the file with var file = GetFile(id);. file.SomeData is a Byte Array (byte[]) and file.FileNameis string.
Thank you for your post. 'HttpResponseMessage' didn't work for me inside an ApiController, so you saved me.
3

Insted of File Download , Can can store into Stream and Directly Pass for File Download

 [HttpGet]
    [Route("api/getFile")]
    public async  Task<FileResult> GetFile(string Param1,string Param2)
    {
        try
        {
            Stream stream = null;
            string strURL = @"File URL";
            HttpClient client = new HttpClient();
            HttpResponseMessage httpResponse = await client.GetAsync(strURL);
            Stream streamToReadFrom = await httpResponse.Content.ReadAsStreamAsync();
            return File(streamToReadFrom, "{MIME TYPE}");

        }
        catch (Exception ex)
        {

            throw ex;
        }
        finally
        { 
        
        }
    }

Comments

2

I've been wondering if there was a simple way to download a file in a more ... "generic" way. I came up with this.

It's a simple ActionResult that will allow you to download a file from a controller call that returns an IHttpActionResult. The file is stored in the byte[] Content. You can turn it into a stream if needs be.

I used this to return files stored in a database's varbinary column.

    public class FileHttpActionResult : IHttpActionResult
    {
        public HttpRequestMessage Request { get; set; }

        public string FileName { get; set; }
        public string MediaType { get; set; }
        public HttpStatusCode StatusCode { get; set; }

        public byte[] Content { get; set; }

        public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
        {
            HttpResponseMessage response = new HttpResponseMessage(StatusCode);

            response.StatusCode = StatusCode;
            response.Content = new StreamContent(new MemoryStream(Content));
            response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
            response.Content.Headers.ContentDisposition.FileName = FileName;
            response.Content.Headers.ContentType = new MediaTypeHeaderValue(MediaType);

            return Task.FromResult(response);
        }
    }

1 Comment

A brief explanation of how your code fixes the OP's problem(s) would enhance the quality of your answer.
1

Another way to download file is to write the stream content to the response's body directly:

[HttpGet("pdfstream/{id}")]
public async Task  GetFile(long id)
{        
    var stream = GetStream(id);
    Response.StatusCode = (int)HttpStatusCode.OK;
    Response.Headers.Add( HeaderNames.ContentDisposition, $"attachment; filename=\"{Guid.NewGuid()}.pdf\"" );
    Response.Headers.Add( HeaderNames.ContentType, "application/pdf"  );            
    await stream.CopyToAsync(Response.Body);
    await Response.Body.FlushAsync();           
}

Comments

0

My attempt works as:

public HttpResponseMessage GetFileFromPath2(string guid)
        {
            var response = new HttpResponseMessage();            
            MemoryStream memoryStream = new MemoryStream();
            var path = "C:\test";
            if (_fileHelper.ExtistFileFromPath(path, guid))
            {
                        memoryStream = new MemoryStream(File.ReadAllBytes(Path.Combine(path, guid)));

                        response.StatusCode = HttpStatusCode.OK;
                        response.Content = new StreamContent(memoryStream);
                        
                        response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") { FileName = guid };
                        response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
                        response.Headers.AcceptRanges.Add("bytes");
            }
            else
            {
                response.StatusCode = HttpStatusCode.NoContent;
                response.Content = new StringContent("File not found");
            }

            return response;
        }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.