Search code examples
c#asp.net-core-webapiasp.net-core-8

Download Pdf/Image/Any file in ASP.NET Core 8 where file is coming from Web api's HttpResponseMessage.Content


I'm facing a problem downloading the file in ASP.NET Core or maybe I'm not writing the right code.

I have an ASP.NET Core Web API with some methods to GetAllFiles, Upload and Download files.

I have one method in my API's controller to download the file (code shown here):

[HttpGet("{id}")]
public async Task<IActionResult> DownloadFile(Guid id)
{
    var fileInDb = pdfRepository.GetByIdAsync(x => x.Pdf_Pk == id, false);

    if (fileInDb == null)
    {
        return NotFound();
    }

    // var fileName = _data.PdfDetail.Where(x => x.Pdf_Pk == id).Select(x => x.FileName).FirstOrDefault();
    var filePath = Path.Combine(Directory.GetCurrentDirectory(), "root\\Files", fileInDb.Result.FileName);
    var provider = new FileExtensionContentTypeProvider();

    if (!provider.TryGetContentType(filePath, out var contentType))
    {
        contentType = "application/octet-stream";
    }

    var bytes = await System.IO.File.ReadAllBytesAsync(filePath);
    return File(bytes, contentType, Path.GetFileName(filePath));            
}

Here I'm returning the file.

I'm calling this method from my web app's controller.

Code for Download method:

public async Task<IActionResult> Download(Guid Id)
{
   return RedirectToAction("Index" ,await PdfServices.DownloadAsync<ApiResponse>(Id));
}

Here PdfServices.DownloadAsync is the service method, the code shown here:

public Task<T> DownloadAsync<T>(Guid Id)
{
    return SendAsync<T>(new ApiRequest()
    {
        type = APIRequestHeader.APIRequest.ApiType.Get,
        Data = Id,
        Url = ApiUrl + "/api/PdfManager/"+Id
    });
}

Now I'm calling this SendAsync method which is used to send requests to API methods and find the content, and I'm performing, serializing/deserializing here

public async Task<T> SendAsync<T>(ApiRequest request)
{
    try
    {
        var client = httpClientFactory.CreateClient("PdfManager");
        HttpRequestMessage message = new();
        message.Headers.Add("Accept", "Applicaiton/Json");
        message.RequestUri = new Uri(request.Url);

        if (request.Data != null)
        {
            message.Content = new StringContent(JsonConvert.SerializeObject(request.Data), Encoding.UTF8, "Application/Json");
        }

        switch(request.type)
        {
            case APIRequest.ApiType.Post:
                message.Method = HttpMethod.Post;
                break;

            case APIRequest.ApiType.Put:
                message.Method = HttpMethod.Put;
                break;

            case APIRequest.ApiType.Delete:
                message.Method = HttpMethod.Delete;
                break;

            default:
                message.Method = HttpMethod.Get;
                break;
        }

        HttpResponseMessage hrm = null;
        hrm = await client.SendAsync(message);
        var apiContent = await hrm.Content.ReadAsStringAsync();

        if (true)   // don't know what to write in this, to download this file
        {
            var readByte = hrm.Content.ReadAsByteArrayAsync();                    

            using(MemoryStream ms =new MemoryStream(readByte.Result))
            {
                var writer = new BinaryWriter(ms, Encoding.UTF8, false);

                var res = new FileContentResult(readByte.Result, hrm.Content.Headers.ContentType.MediaType);
                //return (T)res;
                var test = JsonConvert.DeserializeObject<T>(res);
            }
        }
        
        var apiResponse = JsonConvert.DeserializeObject<T>(apiContent);

        return apiResponse;
    }
    catch (Exception ex)
    {
        var dto = new ApiResponse
        {
            ErrorMessage = new List<string> { Convert.ToString(ex.ToString())},
            IsSuccess = false
        };
        var res = JsonConvert.SerializeObject(dto);
        var ApiResponse = JsonConvert.DeserializeObject<T>(res);

        return ApiResponse;
    }
}

Now, method will be used for all operations like GettingAllFiles, UploadingFiles, DeletingFiles and also DownloadFile.

Because we are returning the file from the API's controller, how can I download that file?

I'm not able to read content as string:

 hrm.Content.ReadAsStringAsync();

because our content is not a string, it contains the files

If I read it as ByteArray, then I'm not able to deserialize the object because it'll ask for a type.

How can I download this file coming into the content from API's controller? How should I read it, what to return?


Solution

  • Commented:

    //this always returns type ApiResponse
    public async Task<ApiResponse> SendAsync(ApiRequest request)
    {
        try
        {
            var client = httpClientFactory.CreateClient("PdfManager");
            HttpRequestMessage message = new();
            message.Headers.Add("Accept", "Applicaiton/Json"); //this is spelled wrong and apparently you accept all kinds of stuff, not just json
            message.RequestUri = new Uri(request.Url);
    
            if (request.Data != null)
                message.Content = new StringContent(JsonConvert.SerializeObject(request.Data), Encoding.UTF8, "Application/Json");
    
            message.Method = request.type switch
            {
                APIRequest.ApiType.Post => HttpMethod.Post,
                APIRequest.ApiType.Put => HttpMethod.Put,
                APIRequest.ApiType.Delete => HttpMethod.Delete,
                _ => HttpMethod.Get,
            };
    
            var hrm = await client.SendAsync(message);
    
            //if it's json, deserialize as before and fuck off
            if (hrm.Content.Headers.ContentType?.MediaType == MediaTypeNames.Application.Json)
            {
                var apiContent = await hrm.Content.ReadAsStringAsync();
                return JsonConvert.DeserializeObject<ApiResponse>(apiContent);
            }
    
            //otherwise, who knows what it is. just give back the
            //content stream for the callsite to decide
            return new ApiResponse()
            {
                StatusCode = hrm.StatusCode,
                IsSuccess = hrm.IsSuccessStatusCode,
                Result = await hrm.Content.ReadAsStreamAsync(),
                ErrorMessage = new(),
            };
        }
        catch (Exception ex)
        {
            //not sure what's happening here
            var dto = new ApiResponse
            {
                ErrorMessage = new List<string> { Convert.ToString(ex.ToString())},
                IsSuccess = false
            };
            var res = JsonConvert.SerializeObject(dto);
            var ApiResponse = JsonConvert.DeserializeObject<ApiResponse>(res);
    
            return ApiResponse;
        }
    }
    

    Now when you call it, you already know you’re getting a PDF, so you cast the object to a stream and do something with it:

    public async Task<IActionResult> DownloadAsync(Guid Id)
    {
        var response = await SendAsync(new ApiRequest()
        {
            type = APIRequestHeader.APIRequest.ApiType.Get,
            Data = Id,
            Url = ApiUrl + "/api/PdfManager/" + Id
        });
    
        if (!response.IsSuccess)
            return new ContentResult() { StatusCode = 500, Content = "fucked" };
    
        using var pdfStream = response.Result as Stream;
        return new FileStreamResult(pdfStream, "application/pdf") { FileDownloadName = "Here's the document you wanted.pdf" };
    }
    

    Or something like that?