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?
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?