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?
Related
I have two questions:
1.When i use postman to upload an image, for example when i browse 15.jpg, it generates something like nDt3Vxjca/15.jpg in value column link
what is nDt3Vxjca.
public async Task<IActionResult> Upload([FromForm] IFormFileViewModel request)
{
var requestContent = new MultipartFormDataContent();
if (request.image!= null)
{
byte[] data;
using (var br = new BinaryReader(request.image.OpenReadStream()))
{
data = br.ReadBytes((int)request.image.OpenReadStream().Length);
}
ByteArrayContent bytes = new ByteArrayContent(data);
requestContent.Add(bytes, "image", request.image.FileName);
};
var client = _httpClientFactory.CreateClient();
string clientID = "abcdefg";
client.DefaultRequestHeaders.Add("Authorization", "Client-ID " + clientID);
var response = await client.PostAsync("https://api.imgur.com/3/upload", requestContent);
return Ok();
}
I copy a some lines of code to convert IformFile to binary. It still manages to upload the image but it doesn't point to my account, it returns something like this:
{"status":200,"success":true,"data":{"id":"I5oGuBd","deletehash":"qVwP3BONUUU9dr7","account_id":null,"account_url":null,"ad_type":null
account_id and account_url is null, Did i make mistake somewhere?
i can't really find a way to download a 100mb zip file from the server to the client and also show the progress while downloading. So how will this look for a normal api controller i can add to my server-side project? if lets say i have 3 files i want to download at 50mb each.
i have tried using JSInterop like this, but this is not showing the progress of the file download, and how will i do if i want to download 3 seperate files at the same time?
try
{
//converting file into bytes array
var dataBytes = System.IO.File.ReadAllBytes(file);
await JSRuntime.InvokeVoidAsync(
"downloadFromByteArray",
new
{
ByteArray = dataBytes,
FileName = "download.zip",
ContentType = "application/force-download"
});
}
catch (Exception)
{
//throw;
}
JS:
function downloadFromByteArray(options: {
byteArray: string,
fileName: string,
contentType: string
}): void {
// Convert base64 string to numbers array.
const numArray = atob(options.byteArray).split('').map(c => c.charCodeAt(0));
// Convert numbers array to Uint8Array object.
const uint8Array = new Uint8Array(numArray);
// Wrap it by Blob object.
const blob = new Blob([uint8Array], { type: options.contentType });
// Create "object URL" that is linked to the Blob object.
const url = URL.createObjectURL(blob);
// Invoke download helper function that implemented in
// the earlier section of this article.
downloadFromUrl({ url: url, fileName: options.fileName });
// At last, release unused resources.
URL.revokeObjectURL(url);
}
UPDATE:
if im using this code, it will show me the progress of the file. But how can i trigger it from my code? This way does not do it. But typing the url does.
await Http.GetAsync($"Download/Model/{JobId}");
Controller
[HttpGet("download/model/{JobId}")]
public IActionResult DownloadFile([FromRoute] string JobId)
{
if (JobId == null)
{
return BadRequest();
}
var FolderPath = $"xxxx";
var FileName = $"Model_{JobId}.zip";
var filePath = Path.Combine(environment.WebRootPath, FolderPath, FileName);
byte[] fileBytes = System.IO.File.ReadAllBytes(filePath);
return File(fileBytes, "application/force-download", FileName);
}
UPDATE 2!
i have got it download with progress and click with using JSInterop.
public async void DownloadFiles()
{
//download all selectedFiles
foreach (var file in selectedFiles)
{
//download these files
await JSRuntime.InvokeAsync<object>("open", $"Download/Model/{JobId}/{file.Name}", "_blank");
}
}
Now the only problem left is.. it only downloads the first file out of 3.
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
};
}
I have the following two methods that handles taking photos from a camera and picking photos from a library. They're both similar methods as at the end of each method, I get an ImageSource back from the Stream and I pass it onto another page which has an ImageSource binding ready to be set. These two method work perfectly. The next step now is to save the Image in SQLite so I can show the images in a ListView later on. My question for the XamGods (Xamarin Pros =), what is the best way to save image in SQLite in 2019? I have been in the forums for hours and I still don't have a tunnel vision on what I want to do. I can either
Convert Stream into an array of bytes to save in Sqlite.
Convert ImageSource into an array of bytes (messy/buggy).
Somehow retrieve the actual Image selected/taken and convert that into an array of bytes into SQLite
I'm sorry if my question is general, but Xamarin does not provide a clear-cut solution on how to save images in SQLite and you can only find bits and pieces of solutions throughout the forums listed below.
How to save and retrieve Image from Sqlite
Load Image from byte[] array.
Creating a byte array from a stream
Thank you in advance!
private async Task OnAddPhotoFromCameraSelected()
{
Console.WriteLine("OnAddPhotoFromCameraSelected");
var photo = await Plugin.Media.CrossMedia.Current.TakePhotoAsync(new Plugin.Media.Abstractions.StoreCameraMediaOptions() { });
var stream = photo.GetStream();
photo.Dispose();
if (stream != null)
{
ImageSource cameraPhotoImage = ImageSource.FromStream(() => stream);
var parms = new NavigationParameters();
parms.Add("image", cameraPhotoImage);
var result = await NavigationService.NavigateAsync("/AddInspectionPhotoPage?", parameters: parms);
if (!result.Success)
{
throw result.Exception;
}
}
}
private async Task OnAddPhotoFromLibrarySelected()
{
Console.WriteLine("OnAddPhotoFromLibrarySelected");
Stream stream = await DependencyService.Get<IPhotoPickerService>().GetImageStreamAsync();
if (stream != null)
{
ImageSource selectedImage = ImageSource.FromStream(() => stream);
var parms = new NavigationParameters();
parms.Add("image", selectedImage);
parms.Add("stream", stream);
var result = await NavigationService.NavigateAsync("/AddInspectionPhotoPage?", parameters: parms);
if (!result.Success)
{
throw result.Exception;
}
}
}
As Jason said that you can save image path into sqlite database, but if you still want to save byte[] into sqlite database, you need to convert stream into byte[] firstly:
private byte[] GetImageBytes(Stream stream)
{
byte[] ImageBytes;
using (var memoryStream = new System.IO.MemoryStream())
{
stream.CopyTo(memoryStream);
ImageBytes = memoryStream.ToArray();
}
return ImageBytes;
}
Then load byte[] from sqlite, converting into stream.
public Stream BytesToStream(byte[] bytes)
{
Stream stream = new MemoryStream(bytes);
return stream;
}
For simple sample, you can take a look:
Insert byte[] in sqlite:
private void insertdata()
{
var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "sqlite1.db3");
using (var con = new SQLiteConnection(path))
{
Image image = new Image();
image.Content = ConvertStreamtoByte();
var result = con.Insert(image);
sl.Children.Add(new Label() { Text = result > 0 ? "insert successful insert" : "fail insert" });
}
}
Loading image from sqlite:
private void getdata()
{
var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "sqlite1.db3");
using (var con = new SQLiteConnection(path))
{
var image= con.Query<Image>("SELECT content FROM Image ;").FirstOrDefault();
if(image!=null)
{
byte[] b = image.Content;
Stream ms = new MemoryStream(b);
image1.Source = ImageSource.FromStream(() => ms);
}
}
}
Model:
public class Image
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public string FileName { get; set; }
public byte[] Content { get; set; }
}
An ASP.NET MVC solution that was working fine in VS 2012 stopped working in 2019 and I cannot find what has changed to break it.
Originally I had the code in the first block - the async task would go to the file storage and retrieve the file, and then the file was sent to the browser using a FileResult that the controller called automatically. After a VERY painful change to VS 2019 and updating everything (.NET runtime, 3rd party libraries, etc.) I have the application working again except for this issue.
I tried creating a new FileStreamResult (which is in the 2nd block) but that does not get called either. When I click on a link that calls this:
<a href="/Cert/File?folder=&filename=#HttpUtility.UrlEncode(FilePath)" ...
It gives me a blank page instead of downloading the file as it used to.
public async Task FileAsync(string folder, string filename)
{
AsyncManager.OutstandingOperations.Increment();
var ReadObjectTask = _fileStorageProvider.ReadObjectDataAsync(folder, filename);
Stream ROResult = await ReadObjectTask;
AsyncManager.Parameters["stream"] = ROResult;
AsyncManager.Parameters["filename"] = filename;
AsyncManager.OutstandingOperations.Decrement();
}
public FileResult FileCompleted(Stream stream, string filename)
{
if (stream == null)
{
return File(Server.MapPath(Url.Content("~/Content/bad_file.png")), "image/png");
}
var file = new FileStreamResult(stream, MIMEAssistant.GetMIMEType(filename));
if (filename.Contains("/"))
{
filename = filename.Split('/').Last();
}
file.FileDownloadName = filename;
return file;
}
Here is the FileStreamResult I tried:
public System.Web.Mvc.FileStreamResult FileCompleted(Stream stream, string contentType, string filename)
{
if (stream == null)
{
string bFile = Server.MapPath(Url.Content("~/Content/bad_file.png"));
Stream blankfile = System.IO.File.OpenRead(bFile);
return File(blankfile, MIMEAssistant.GetMIMEType(bFile), System.IO.Path.GetFileName(bFile));
}
if (filename.Contains("/"))
{
filename = filename.Split('/').Last();
}
return File(stream, MIMEAssistant.GetMIMEType(filename), filename);
}
(The filename.Contains part is old code from a predecessor that I just need to replace with Path.GetFileName - sorry I did not clean it up before I posted.)
I decided to make the Async Task one of type and moved the
stream processing into that procedure to solve my problem. I do not know
why the Async Task that was working in 2012 stopped in 2019.
public async Task<FileResult> FileAsync(string folder, string filename)
{
AsyncManager.OutstandingOperations.Increment();
var ReadObjectTask = _fileStorageProvider.ReadObjectDataAsync(folder, filename);
Stream ROResult = await ReadObjectTask;
AsyncManager.Parameters["stream"] = ROResult;
AsyncManager.Parameters["filename"] = filename;
AsyncManager.OutstandingOperations.Decrement();
if (ROResult == null)
{
return File(Server.MapPath(Url.Content("~/Content/bad_file.png")), "image/png");
}
var file = new FileStreamResult(ROResult, MIMEAssistant.GetMIMEType(filename));
file.FileDownloadName = System.IO.Path.GetFileName(filename);
return file;
}