You can just "tunnel" the responses in binary.
Example project: https://github.com/datvm/Example-Tunnel-Binary-API
Assume Api2
is something like this (of course in real life it could be anything, even if it's not ASP.NET, as long as it returns binary):
[Route("api2")]
public IActionResult Api2()
{
var filePath = Path.Combine(
Path.GetDirectoryName(typeof(FileController).Assembly.Location),
"background.png");
var fileStream = System.IO.File.OpenRead(filePath);
return this.File(fileStream, "image/png");
}
API 1 can simply call API 2 and get the binary content:
// This API call another API to get the file, it cannot access the file
[Route("api1")]
public async Task<IActionResult> Api1()
{
// In production you should not initialize HttpClient yourself
using (var httpClient = new HttpClient())
{
// Url should be a server in production
var request = new HttpRequestMessage(HttpMethod.Get, "https://localhost:44322/file/api2");
using (var response = await httpClient.SendAsync(request))
{
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsByteArrayAsync();
return this.File(content, "image/png", "background.png");
}
}
}
The client-side Javascript, you can do anything with the file:
<h1>SPA Page</h1>
<button id="btn-download">Click to download</button>
<button id="btn-download-new">Click to download in new window</button>
<button id="btn-download-fetch">Click to process with fetch</button>
<script>
document.querySelector("#btn-download").addEventListener("click", () => {
window.location.href = "/file/api1";
});
document.querySelector("#btn-download-new").addEventListener("click", () => {
window.open("/file/api1");
});
document.querySelector("#btn-download-fetch").addEventListener("click", async () => {
let data = await fetch("/file/api1")
.then(response => response.arrayBuffer());
// Do whatever you want with it
alert(data.byteLength);
});
</script>