In Blazor how do you load an image into memory so C# can access it - webapi

I am sure this is something simple that I have missed, but I need to load an image from WASM Blazor from the webserver so that I can print it to a PDF file. Printing isn't an issue. I just need to load it into a memory stream or something I can convert to a memory stream.

I dug around long enough that I found the answer to this issue. I was being stupid, but just in case someone needs to know the answer here it is:
public class DownloadBinFile
{
private HttpClient client;
public DownloadBinFile(HttpClient http_)
{
client = http_;
}
public async Task<Stream> Download(string url)
{
var response = await client.GetAsync($"{url}");
response.EnsureSuccessStatusCode();
Stream ms;
ms = await response.Content.ReadAsStreamAsync();
return ms;
}
}

Related

Serve file from Google Storage files via asp.net core webapi

I have a website (webapi+c#+asp.net core) that serving files to the clients. The application expose the method to the Download file from the server.
The actual file is stored on Google Storage. So, the file is being downloaded to the server (stored in memory stream) and then returned to the caller.
This is my code:
[Route("download/{id}")]
[HttpGet]
public async Task<ActionResult> DownloadAsync(string id)
{
// Authentication...
Uri remoteFile = item.GetEnpointResponse(); // Path to file in bucket
using (StorageClient gcpStorage = await StorageClient.CreateAsync(remoteFile.GetCredentials().Credentials).ConfigureAwait(false))
{
MemoryStream ms = new MemoryStream();
await gcpStorage.DownloadObjectAsync("bucketName", "path/to/blob", ms, new DownloadObjectOptions
{
EncryptionKey = "XXX"
}).ConfigureAwait(false);
ms.Seek(0, SeekOrigin.Begin);
return File(ms, "application/json");
}
}
Two problems I found:
It's storing all data of the file in the memory. If the file is large... its a hard job.
Waste of time - The file is being downloaded twice until getting to the client's hands.
Since this code is happening many times, I wonder if I can improve the performance of it? Something that I can improve here?
Most elegant way I could find is the following, using the library AspNetCore.Proxy:
namespace Storage
{
[Route("api/storage")]
public class StorageController : Controller
{
private readonly string _bucketName;
private readonly GoogleCredential _credential;
private readonly UrlSigner _urlSigner;
public StorageController(GoogleCredential credential, string bucketName)
{
_credential = credential;
_bucketName = bucketName;
_urlSigner = UrlSigner.FromServiceAccountCredential(_credential.UnderlyingCredential as ServiceAccountCredential);
}
[HttpGet("download/{file}")]
public async Task DownloadFileAsync(string file)
{
using var storageClient = await StorageClient.CreateAsync(_credential);
var signUrl = await _urlSigner.SignAsync(
_bucketName,
file,
TimeSpan.FromHours(3),
HttpMethod.Get
);
await this.HttpProxyAsync(signUrl);
}
}
}

How do I read and update HttpResponse body using PipeWriter?

This is actually a 2-part question related directly to .net core 3.0 and specifically with PipeWriter: 1) How should I read in the HttpResponse body? 2) How can I update the HttpResponse? I'm asking both questions because I feel like the solution will likely involve the same understanding and code.
Below is how I got this working in .net core 2.2 - note that this is using streams instead of PipeWriter and other "ugly" things associated with streams - eg. MemoryStream, Seek, StreamReader, etc.
public class MyMiddleware
{
private RequestDelegate Next { get; }
public MyMiddleware(RequestDelegate next) => Next = next;
public async Task Invoke(HttpContext context)
{
var httpResponse = context.Response;
var originalBody = httpResponse.Body;
var newBody = new MemoryStream();
httpResponse.Body = newBody;
try
{
await Next(context);
}
catch (Exception)
{
// In this scenario, I would log out the actual error and am returning this "nice" error
httpResponse.StatusCode = StatusCodes.Status500InternalServerError;
httpResponse.ContentType = "application/json"; // I'm setting this because I might have a serialized object instead of a plain string
httpResponse.Body = originalBody;
await httpResponse.WriteAsync("We're sorry, but something went wrong with your request.");
return;
}
// If everything worked
newBody.Seek(0, SeekOrigin.Begin);
var response = new StreamReader(newBody).ReadToEnd(); // This is the only way to read the existing response body
httpResponse.Body = originalBody;
await context.Response.WriteAsync(response);
}
}
How would this work using PipeWriter? Eg. it seems that working with pipes instead of the underlying stream is preferable, but I can not yet find any examples on how to use this to replace my above code?
Is there a scenario where I need to wait for the stream/pipe to finish writing before I can read it back out and/or replace it with a new string? I've never personally done this, but looking at examples of PipeReader seems to indicate to read things in chunks and check for IsComplete.
To Update HttpRepsonse is
private async Task WriteDataToResponseBodyAsync(PipeWriter writer, string jsonValue)
{
// use an oversized size guess
Memory<byte> workspace = writer.GetMemory();
// write the data to the workspace
int bytes = Encoding.ASCII.GetBytes(
jsonValue, workspace.Span);
// tell the pipe how much of the workspace
// we actually want to commit
writer.Advance(bytes);
// this is **not** the same as Stream.Flush!
await writer.FlushAsync();
}

Asp Core serve file via Web Api

I am trying to figure out how i can transfer a file from my web api to my angular 5 front end and download it.
My Angular 5 front end is simple (i am using FileSaver package to help):
filedownload.component.html
<button (click)="downloadFile(document)">Download</button>
filedownload.component.ts
downloadFile(document) {
this.http.get("http://localhost:8080/"+document.id+).subscribe(
data => {
const dataFile = data;
saveAs(dataFile, document.filename);
},
err => { console.info(err); }
);
}
On my web api i have no idea how to construct the response.
So far i only have:
[HttpGet("{id}"]
public async Task<IActionResult> GetFile(Guid id) {
var testFileInfo = _dbcontext.UploadedFile.GetById(id);
var filePath = Path.Combine("C:\FileRepo", testFileInfo.Filename);
// what do i do here? i got no clue
throw new NotImplementedException();
}
I have tried experimenting with various examples online but nothing seems to work.
The idea is that web api would serve any range of files back to the front end, depending on whats on server.
File sizes range from 100kb to 50mb at this stage, file can be bigger once i implement archiving and zipping of multiple files.
If you want your API to return a file you can use this simple code:
[HttpGet("{id}")]
public IActionResult Get(Guid id)
{
var bytes = System.IO.File.ReadAllBytes(#"YourFilePAth");
return File(bytes, "application/octet-stream", "YourFileName.extension");
}
It is wiser to use a stream:
[HttpGet("{id}")]
public IActionResult Get(Guid id)
{
var stream = System.IO.File.OpenRead(#"YourFilePAth");
return File(stream, "application/octet-stream", "YourFileName.extension");
}

Web API Async Upload with XmlHttpRequest to get progress

I'm trying to drag and drop file upload with a progress bar.
I have a div which is listening to files being dropped on which is working perfectly.
I'm then..
//Setting up a XmlHttpRequest
xhr = new XMLHttpRequest();
//Open connection
xhr.open("post", "api/ImageUpload", true);
// Set appropriate headers
xhr.setRequestHeader("Content-Type", "multipart/form-data");
xhr.setRequestHeader("X-File-Type", uf.type);
xhr.setRequestHeader("X-File-Name", uf.name);
xhr.setRequestHeader("X-File-Size", uf.size);
This sends fine, with the stream as the body of the request to the Web API (not async).
[System.Web.Mvc.HttpPost]
public string Post()
{
Stream stream = HttpContext.Current.Request.InputStream;
String filename = HttpContext.Current.Request.Headers["X-File-Name"];
FileModel file = uploadService.UploadFile(stream, filename);
return file.Id.ToString();
}
I'm trying to chance the request to "public async Task< string> Post(){ }
If the method was using a multipart form on the page instead of XmlHttpRequest I would have used "await Request.Content.ReadAsMultipartAsync(provider)" but this doesn't seem to be populated at the time I need it.
So what is the correct was to handle and an Async call from XmlHttpRequest on a Web API in order to record progress during the request with XHR's progress event?
I have looked at a great deal of pages so far to find a solution but this is the page I have used primarily.
http://robertnyman.com/html5/fileapi-upload/fileapi-upload.html
Thanks for any help
Oliver
It looks like someone else had the same question with you and got an answer yet. please have a look at ASP.NET MVC 4 Web Api ajax file upload.
And here is an example from microsoft http://www.asp.net/web-api/overview/working-with-http/sending-html-form-data,-part-2.
I combined the two above solution together and worked for me (just adjust a little bit)
one line change in Javascritp
xhr.open("post", "api/upload", true);
Save the file using stream
public class UploadController : ApiController
{
public async Task<HttpResponseMessage> PostFormData()
{
string root = HttpContext.Current.Server.MapPath("~/App_Data");
var fileName = Path.Combine(root, Request.Headers.GetValues("X-File-Name").First());
try
{
var writer = new StreamWriter(fileName);
await Request.Content.CopyToAsync(writer.BaseStream);
writer.Close();
return Request.CreateResponse(HttpStatusCode.OK);
}
catch (System.Exception e)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e);
}
}
}

Is there a notification when ASP.NET Web API completes sending to the client

I'm using Web API to stream large files to clients, but I'd like to log if the download was successful or not. That is, if the server sent the entire content of the file.
Is there some way to get a a callback or event when the HttpResponseMessage completes sending data?
Perhaps something like this:
var stream = GetMyStream();
var response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StreamContent(stream);
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
// This doesn't exist, but it illustrates what I'm trying to do.
response.OnComplete(context =>
{
if (context.Success)
Log.Info("File downloaded successfully.");
else
Log.Warn("File download was terminated by client.");
});
EDIT: I've now tested this using a real connection (via fiddler).
I inherited StreamContent and added my own OnComplete action which checks for an exception:
public class StreamContentWithCompletion : StreamContent
{
public StreamContentWithCompletion(Stream stream) : base (stream) { }
public StreamContentWithCompletion(Stream stream, Action<Exception> onComplete) : base(stream)
{
this.OnComplete = onComplete;
}
public Action<Exception> OnComplete { get; set; }
protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
{
var t = base.SerializeToStreamAsync(stream, context);
t.ContinueWith(x =>
{
if (this.OnComplete != null)
{
// The task will be in a faulted state if something went wrong.
// I observed the following exception when I aborted the fiddler session:
// 'System.Web.HttpException (0x800704CD): The remote host closed the connection.'
if (x.IsFaulted)
this.OnComplete(x.Exception.GetBaseException());
else
this.OnComplete(null);
}
}, TaskContinuationOptions.ExecuteSynchronously);
return t;
}
}
Then I use it like so:
var stream = GetMyStream();
var response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StreamContentWithCompletion(stream, ex =>
{
if (ex == null)
Log.Info("File downloaded successfully.");
else
Log.Warn("File download was terminated by client.");
});
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
return response;
I am not sure if there is direct signaling that all is ok, but you can use a trick to find out that the connection is exist just before you end it up, and right after you fully send the file.
For example the Response.IsClientConnected is return true if the client is still connected, so you can check something like:
// send the file, make a flush
Response.Flush();
// and now the file is fully sended check if the client is still connected
if(Response.IsClientConnected)
{
// log that all looks ok until the last byte.
}
else
{
// the client is not connected, so maybe have lost some data
}
// and now close the connection.
Response.End();
if the server sent the entire content of the file
Actually there is nothing to do :)
This might sound very simplistic but you will know if an exception is raised - if you care about server delivering and not client cancelling halfway. IsClientConnected is based on ASP.NET HttpResponse not the WebApi.

Resources