Something is blocking my thread - Blazor wasm - blazor-client-side

Using MatBlazor I'm trying to upload files. However, something is blocking the thread and the entire application get's blocked. I can't figure out why. It seems like the thread is blocked until the file has been loaded into the memory.
Is it my code or is it the MatFileUploadEntry that is blocking the thread?
Does someone have any idea?
Call:
 
<MatFileUpload OnChange="#FileUpload"></MatFileUpload>
Response:
private async Task FileUpload(IMatFileUploadEntry[] files)
{
var f = files.FirstOrDefault();
if (f.Name.IsValidFileFormat())
{
var file = await GetFileModel(f);
if (f.Name.IsImage())
Model.Image = file;
else
Model.Document = file;
}
}
private async Task<FileModel> GetFileModel(IMatFileUploadEntry f)
{
var sw = new Stopwatch();
sw.Start();
using var ms = new MemoryStream();
await f.WriteToStreamAsync(ms);
sw.Stop();
var base64String = Convert.ToBase64String(ms.ToArray());
return new FileModel
{
FileName = f.Name,
FileContentBase64 = base64String
};
}

Related

Return a file from asp.net web api

I've been trying to return a file from my web api. Below is my code somehow downloads the file, but the downloaded file is corrupted.
SomeMethod
{
var stream = new MemoryStream();
// processing the stream.
var result = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new ByteArrayContent(response.FileArray.ToArray())
};
result.Content.Headers.ContentDisposition =
new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment")
{
FileName = "CertificationCard.pdf"
};
result.Content.Headers.ContentType =
new MediaTypeHeaderValue("application/octet-stream");
return result;
}
public HttpResponseMessage Download([FromUri] DownloadRequest req) { }

Slow performance on FileStreamResult

I am trying to create a API controller action that returns large file 400+ mb.
Right now i am using a FileStreamResult as IActionResult, which is very slow on the larger files around +900mb but works on files around 4gb.
[HttpGet("{fileId}")]
public async Task<IActionResult> DownloadFile(Guid fileId)
{
var fileResponse = await _bus.RequestAsync<FileRequest, FileResponse>(new FileRequest(fileId));
var file = new FileInfo(#"c:\users\peter\desktop\save\" + fileId.ToString());
return new FileStreamResult(file.OpenRead(), fileResponse.ContentType)
{
FileDownloadName = fileResponse.Filename
};
}
If i copy the file to a memorystream first it is much faster but then i got a limitation and it fails at 4gb files.
[HttpGet("{fileId}")]
public async Task<IActionResult> DownloadFile(Guid fileId)
{
var fileResponse = await _bus.RequestAsync<FileRequest, FileResponse>(new FileRequest(fileId));
var file = new FileInfo(#"c:\users\peter\desktop\save\" + fileId.ToString());
var ms = new MemoryStream();
file.OpenRead().CopyTo(ms);
return new FileStreamResult(ms, fileResponse.ContentType)
{
FileDownloadName = fileResponse.Filename
};
}
How can i return large files fast thru the MVC Api?

Result of the "R Script" without ColumnNames

I'm going crazy!
I'm using Azure Machine Learning and R Script. I deploy it as Web Service. I use sample code based on HttpClient.
using (var client = new HttpClient())
{
var scoreRequest = new
{
Inputs = new Dictionary<string, StringTable>() {
{
"input1",
new StringTable()
{
ColumnNames = new string[] {
"experts_estimates",
"experts_share_of_unique_information",
"avg_correlation",
"point_a",
"point_b",
"is_export_mode"
},
Values = new string[,] {
{
expertsEstimatesStr,
expertsShareOfUniqueInformationStr,
avgCorrelationStr,
pointAStr,
pointBStr,
isExportModeStr
},
}
}
},
},
GlobalParameters = new Dictionary<string, string>()
{
}
};
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);
client.BaseAddress = new Uri(apiUrl);
// WARNING: The 'await' statement below can result in a deadlock
// if you are calling this code from the UI thread of an ASP.Net application.
// One way to address this would be to call ConfigureAwait(false)
// so that the execution does not attempt to resume on the original context.
// For instance, replace code such as:
// result = await DoSomeTask()
// with the following:
// result = await DoSomeTask().ConfigureAwait(false)
HttpResponseMessage response = await client.PostAsJsonAsync("", scoreRequest);
if (response.IsSuccessStatusCode)
{
string result = await response.Content.ReadAsStringAsync();
return result;
}
else
{
// Print the headers - they include the requert ID and the timestamp,
// which are useful for debugging the failure
var headers = response.Headers.ToString();
string responseContent = await response.Content.ReadAsStringAsync();
throw new Exception(responseContent, new Exception(headers));
}
}
and when I run code from Visual Studio I get:
but when I run code from Azure App Service I get:
Any ideas?
One solution is adding "edit metadata" module inside the model and rename the output columns. It'll be easy than using the code to name the columns.

Get FileStream from form posted file

I have a control on view page. When user selects the file and clicks on submit button this makes ajax call to upload the file on server. Unfortunately my server method accepts file path (like C:/Videos/1.mp4) to upload. This works great with string demoPath in the code below but I'm not sure how to get similar path when user selects in control. Due to sercurity reasons modern browsers not allows exposing paths. How to achieve this?
[HttpPost]
public async Task<JsonResult> Upload(string lectureId, string filepath)
{
for (int i = 0; i < Request.Files.Count; i++)
{
//// This works great
//string demoPath = "C:/Users/abchi/Desktop/BigBuckBunny.mp4";
var file = Request.Files[i];
var fileName = Path.GetFileName(file.FileName);
//var path = Path.Combine(Server.MapPath("~/User/"), fileName);
//file.SaveAs(path);
//await RunUploader(demoPath);
await RunUploader(get_path_from_posted_file_or_request);
}
return Json(new { error = false, message = "Video uploaded." });
}
public async Task RunUploader(string filePath)
{
// :::::::
using (var fileStream = new FileStream(filePath, FileMode.Open))
{
// ::::
}
// ::::::
}
I'm not sure this is expected because I did not quite understand.
Download the file path of the user's computer can not be - https://stackoverflow.com/a/15201258/4599089
but if you want to have access to the FileStream on your server:
File has InputStream and you can use this:
[HttpPost]
public async Task<JsonResult> Upload(string lectureId, string filepath)
{
for (int i = 0; i < Request.Files.Count; i++)
{
var file = Request.Files[i];
var fileName = Path.GetFileName(file.FileName);
var path = Path.Combine(Server.MapPath("~/User/"), fileName);
var fileStream = new FileStream(path, FileMode.Create, FileAccess.ReadWrite);
file.InputStream.CopyTo(fileStream);
fileStream.Close();
await RunUploader(path); //path or stream
}
return Json(new { error = false, message = "Video uploaded." });
}
public async Task RunUploader(string filePath)
{
// :::::::
using (var fileStream = new FileStream(filePath, FileMode.Open))
{
// ::::
}
// ::::::
}
I asked my fellow dev to make necessary changes in public async Task RunUploader(string filePath) parameters. Said code was part of YouTube .NET samples for console apps. Now we are developing for web, in this case we can't pass full path. So they made following changes:
[HttpPost]
public async Task<JsonResult> Upload(string lectureId)
{
for (int i = 0; i < Request.Files.Count; i++)
{
var file = Request.Files[i];
Stream fileStream = file.InputStream;
await Run(fileStream);
}
return Json(new { error = false, message = "Video uploaded." });
}
public async Task Run(Stream fileStream)
{
// ::::::::::
using (fileStream)
{
// ::::::
}
// ::::::::::
}
Now with this change everything started working.

Generating PDFs using Phantom JS on .NET applications

I have been looking into phantomJS and looks like it could be a great tool to use generating PDFs. I wonder if anyone have successfully used it for their .NET applications.
My specific question is: how would you use modules like rasterize.js on the server, receive requests and send back generated pdfs as a response.
My general question is: is there any best practice for using phantomJS with .NET Applications. What would be the best way to achieve it?
I am fairly new in .NET World and I would appreciate the more detailed answers. Thanks everyone. :)
I don't know about best practices, but, I'm using phantomJS with no problems with the following code.
public ActionResult DownloadStatement(int id)
{
string serverPath = HttpContext.Server.MapPath("~/Phantomjs/");
string filename = DateTime.Now.ToString("ddMMyyyy_hhmmss") + ".pdf";
new Thread(new ParameterizedThreadStart(x =>
{
ExecuteCommand("cd " + serverPath + #" & phantomjs rasterize.js http://localhost:8080/filetopdf/" + id.ToString() + " " + filename + #" ""A4""");
})).Start();
var filePath = Path.Combine(HttpContext.Server.MapPath("~/Phantomjs/"), filename);
var stream = new MemoryStream();
byte[] bytes = DoWhile(filePath);
return File(bytes, "application/pdf", filename);
}
private void ExecuteCommand(string Command)
{
try
{
ProcessStartInfo ProcessInfo;
Process Process;
ProcessInfo = new ProcessStartInfo("cmd.exe", "/K " + Command);
ProcessInfo.CreateNoWindow = true;
ProcessInfo.UseShellExecute = false;
Process = Process.Start(ProcessInfo);
}
catch { }
}
public ViewResult FileToPDF(int id)
{
var viewModel = file.Get(id);
return View(viewModel);
}
private byte[] DoWhile(string filePath)
{
byte[] bytes = new byte[0];
bool fail = true;
while (fail)
{
try
{
using (FileStream file = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
bytes = new byte[file.Length];
file.Read(bytes, 0, (int)file.Length);
}
fail = false;
}
catch
{
Thread.Sleep(1000);
}
}
System.IO.File.Delete(filePath);
return bytes;
}
Here is the action flow:
The user clicks on a link to DownloadStatement Action. Inside there, a new Thread is created to call the ExecuteCommand method.
The ExecuteCommand method is responsible to call phantomJS. The string passed as an argument do the following.
Go to the location where the phantomJS app is and, after that, call rasterize.js with an URL, the filename to be created and a print format. (More about rasterize here).
In my case, what I really want to print is the content delivered by the action filetoupload. It's a simple action that returns a simple view. PhantomJS will call the URL passed as parameter and do all the magic.
While phantomJS is still creating the file, (I guess) I can not return the request made by the client. And that is why I used the DoWhile method. It will hold the request until the file is created by phantomJS and loaded by the app to the request.
If you're open to using NReco.PhantomJS, which provides a .NET wrapper for PhantomJS, you can do this very succinctly.
public async Task<ActionResult> DownloadPdf() {
var phantomJS = new PhantomJS();
try {
var temp = Path.Combine(Path.GetTempPath(),
Path.ChangeExtension(Path.GetRandomFileName(), "pdf")); //must end in .pdf
try {
await phantomJS.RunAsync(HttpContext.Server.MapPath("~/Scripts/rasterize.js"),
new[] { "https://www.google.com", temp });
return File(System.IO.File.ReadAllBytes(temp), "application/pdf");
}
finally {
System.IO.File.Delete(temp);
}
}
finally {
phantomJS.Abort();
}
}
Here's some very basic code to generate a PDF using Phantom.JS but you can find more information here: https://buttercms.com/blog/generating-pdfs-with-node
var webPage = require('webpage');
var page = webPage.create();
page.viewportSize = { width: 1920, height: 1080 };
page.open("http://www.google.com", function start(status) {
page.render('google_home.pdf, {format: 'pdf', quality: '100'});
phantom.exit();
});

Resources