arivera12 / blazordownloadfile Goto Github PK
View Code? Open in Web Editor NEWBlazor download files to the browser from c# without any javascript library reference or dependency.
License: MIT License
Blazor download files to the browser from c# without any javascript library reference or dependency.
License: MIT License
e.g download csv file and save,
Open in winmerge (Or some other tool that supports byte only alignment)
observe a null character at the end of the file.
This fails a "Signature check" because csv have to be ASCII chars only, i.e Alphanumerics with few extras (".,-) etc.
non-printables, back moves, and "Graphics" should fail.
I have a memory stream that is 280803689
bytes
I have direct access to the buffer it contains via var bytes = ms.GetBuffer();
I then call
DowloadFileResult fileResult = await DownloadFileService.DownloadFile(FileDetails.OriginalFileName, bytes, cts.Token, 1024*64, "application/octet-stream", OnProgress);
Then 6 minutes later I get this:
blazor.webassembly.js:1 crit: Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
Unhandled exception rendering component: Out of memory
System.OutOfMemoryException: Out of memory
at (wrapper managed-to-native) System.String.FastAllocateString(int)
at System.String.CreateStringFromEncoding (System.Byte* bytes, System.Int32 byteLength, System.Text.Encoding encoding) <0x2fd3328 + 0x00030> in <filename unknown>:0
at System.Text.Encoding.GetString (System.Byte* bytes, System.Int32 byteCount) <0x2fd3170 + 0x0004e> in <filename unknown>:0
at System.Text.Json.JsonReaderHelper.TranscodeHelper (System.ReadOnlySpan`1[T] utf8Unescaped) <0x32b9e68 + 0x0004a> in <filename unknown>:0
at System.Text.Json.JsonSerializer.WriteCoreString (System.Object value, System.Type type, System.Text.Json.JsonSerializerOptions options) <0x356b5a0 + 0x00074> in <filename unknown>:0
at System.Text.Json.JsonSerializer.Serialize[TValue] (TValue value, System.Text.Json.JsonSerializerOptions options) <0x356b210 + 0x0000c> in <filename unknown>:0
at Microsoft.JSInterop.JSRuntime.InvokeAsync[TValue] (System.String identifier, System.Threading.CancellationToken cancellationToken, System.Object[] args) <0x3563820 + 0x001f4> in <filename unknown>:0
at Microsoft.JSInterop.JSRuntimeExtensions.InvokeVoidAsync (Microsoft.JSInterop.IJSRuntime jsRuntime, System.String identifier, System.Threading.CancellationToken cancellationToken, System.Object[] args) <0xc9b80a0 + 0x0004c> in <filename unknown>:0
at BlazorDownloadFile.BlazorDownloadFileService.DownloadFile (System.String fileName, System.Byte[] bytes, System.Threading.CancellationToken cancellationToken, System.Int32 bufferSize, System.String contentType, System.IProgress`1[T] progress) <0x4f21960 + 0x00246> in <filename unknown>:0
at System.Threading.Tasks.ValueTask`1[TResult].get_Result () <0x9ce4320 + 0x00034> in <filename unknown>:0
at ```
I call Garbage collect before calling the Download function to make sure there is plenty of available memory:
L: GC_MAJOR_SWEEP: major size: 2512K in use: 1523K
L: GC_MAJOR: (user request) time 63.28ms, stw 63.30ms los size: 450736K in use: 449168K
I have tried to use the `Memory stream` **directly** but it just drop 5MB of the file after about 3 minutes.
Also the `Progress Callback` is **only** called once !
hi i justinstall the package and import in my program.cs the
builder.Services.AddBlazorDownloadFile();
and when i am trying to use this line in a blazor page
[Inject]
public IBlazorDownloadFileService BlazorDownloadFileService { get; set; }
i got this error:
Error CS0246 Type or namespace name 'IBlazorDownloadFileService' not found (is a using directive or assembly reference missing?)
i try too using the @Inject IBlazorDownloadFileService BlazorDownloadFileService but stilll got the same error.
i am using WPA with 5.NET
If the previous download had been cancelled (e.g. Due to a user moving away from the page etc),
then the next call to
await DownloadFileService.DownloadFile(fileDetails.OriginalFileName, stream, cancellationToken, cancellationToken, bufferSize: 32*1024);
Would also include the previous files buffers ?
Hi!
Since a week we are having issues with downloading files in Firefox.
I have this piece of code:
public async override Task DownloadAsync(bool confirmed = false)
{
using FbController2 fbController = new FbController2();
var angebot = await Angebot.GetAngebotAsync(Angebotsnummer, fbController);
if (angebot is not null)
{
PrintAngebotRegelsatz printRegeln = new PrintAngebotRegelsatz
{
ShowFooter = true,
ShowHeader = true
};
PrintAngebot angebotPrinter = await PrintAngebot.CreateAsync(angebot, printRegeln, fbController);
// Generates th PDF file and returns the full file path
string filenameAngebotPdf = angebotPrinter.Print(GlobalConfig.Configuration.OutputPfad);
await downloadService.DownloadFile($"AN-{Angebotsnummer}.pdf", await File.ReadAllBytesAsync(filenameAngebotPdf), "application/pdf");
await downloadService.ClearBuffers();
}
_isDownloading = false;
}
As you can see, I want to download AN-XXXXX.pdf which becomes generated one line before. Downloading the PDF will result in Firefox redircet to a blob:// url which displays the PDF.
blob:https://localhost:5001/275606a8-d314-4674-9f2d-44fa282e4da9
I'm running Windows 10 Professional 21H1 64 Bit with Firefox 98.0.2 with German locale. It works in Chrome and Edge.
When downloading a file from a server directly to JavaScript or Blazor the browser has it's own memory and hardware limitations.
We should use Content-Disposition
attachment response header as it has more cross-browser compatibility.
When using Content-Disposition attachment response header the backing store that the browser uses its the local disk.
This way we will bypass the limitations of the Max File Size of JavaScript and Blazor.
To make this work with seamless integration we would need to create a JavaScript function which:
window
form
inside the new window
with
action
="specified url"method
="specified method"enctype
="specified encode type"inputs
if an object is provided as argumentform
in that window
to the specified urlNot sure if I am missing something else but this should trigger the file download process within the browser provider and no data should be loaded into memory to JavaScript or Blazor and go straight to the device local disk instead.
Iframe will only work on sites that are on the same domain or sites with Access-Control-Allow-Origin response header
If neither of them are considered or present we will hit for sure the same-origin policy error.
Authorization tokens may be passed down to the new window
and form
as an input
value to be submitted with the rest of the other inputs values.
AFAIK there is no way to pass down authorization token in a header when submitting a form or sending cross domain cookies.
https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_win_open3
https://gist.github.com/carlosrivera/768b0c14e083c1ab423f
Still a bit early, but this looks promising as it allows a "stream" to be used
https://web.dev/native-file-system/
It also has a drop back to "Legacy mode", https://github.com/GoogleChromeLabs/browser-nativefs
Thoughts ?
I was having an issue with an even smaller size (400kb ish)
I turned my stream into a byte array and it worked.
@arivera12, you might want to impliment this
byte[] myByteArray;
using (var memoryStream = new MemoryStream())
{
originalStream.CopyTo(memoryStream);
myByteArray= memoryStream.ToArray();
}
for your
internal static async Task<byte[]> ToByteArrayAsync(this Stream stream)
{
var streamLength = (int)stream.Length;
var data = new byte[streamLength];
stream.Position = 0;
await stream.ReadAsync(data, 0, streamLength);
return data;
}
I can submit a PR if you need/like
Originally posted by @thalaeg in #25 (comment)
We are generating a CSV file with a StringBuilder
.
Once the StringBuilder is build we want to download the generated content as file with BlazorDownloadFile
.
await downloadService.DownloadFileFromText("StarMoney_SepaImport.csv", sb.ToString(), "text/plain");
The file will be downloaded with UTF-8 encoding but we need ISO-8859-1 in our case. So it would be nice if we can pass the encoding to the download methods.
` var buffer = new MemoryStream();
var writer = new BinaryWriter(buffer);
writer.Write("Student Number: " + studentNumber + "\r\n");
writer.Write("Module: " + selectedModule + "\r\n");
writer.Write("ACW: " + selectedACW + "\r\n");
writer.Write("\r\n");
writer.Write("Section 1" + "\r\n");
writer.Write("Section: " + selectedSection1 + "\r\n");
writer.Write("Criteria: " + selectedCriteria1 + "\r\n");
writer.Write("Feedback: " + selectedFeedback1.Trim() + " - " + selectedFeedback1grade + "\r\n");
writer.Write("\r\n");
if (sectionCounter >= 2)
{
writer.Write("Section 2" + "\r\n");
writer.Write("Section: " + selectedSection2 + "\r\n");
writer.Write("Criteria: " + selectedCriteria2 + "\r\n");
writer.Write("Feedback: " + selectedFeedback2.Trim() + " - " + selectedFeedback2grade + "\r\n");
writer.Write("\r\n");
}
if (sectionCounter == 3)
{
writer.Write("Section 3" + "\r\n");
writer.Write("Section: " + selectedSection3 + "\r\n");
writer.Write("Criteria: " + selectedCriteria3 + "\r\n");
writer.Write("Feedback: " + selectedFeedback3.Trim() + " - " + selectedFeedback3grade + "\r\n");
writer.Write("\r\n");
}
int grade = selectedFeedback1grade + selectedFeedback2grade + selectedFeedback3grade;
writer.Write("Overall Grade is: " + grade);
writer.Close();
byte[] bytes = buffer.ToArray();
var task = await BlazorDownloadFileService.DownloadFile($"{studentNumber}.pdf", bytes, 1024 * 64, "application/octet-stream");
}`
Trying to download a PDF but everytime I try top open it, I get an error saying its corrupt or not encoded
Thanks!
I am trying to send a JSON file to user, but looking at the code this is made for more binary type files and not text files.
Version: 2.1.6
AspNetCore: 3.1.8
After adding Content-Security-Policy "script-src 'self' "
header DownloadFile function started throwing the following exception:
Could not find '_blazorDowloadFileBase64String' in 'window'.
Error: Could not find '_blazorDowloadFileBase64String' in 'window'.
at http://192.168.0.30:5000/_framework/blazor.server.js:8:30944
at Array.forEach (<anonymous>)
at p (http://192.168.0.30:5000/_framework/blazor.server.js:8:30904)
at http://192.168.0.30:5000/_framework/blazor.server.js:8:31614
at new Promise (<anonymous>)
at e.beginInvokeJSFromDotNet (http://192.168.0.30:5000/_framework/blazor.server.js:8:31587)
at http://192.168.0.30:5000/_framework/blazor.server.js:1:20052
at Array.forEach (<anonymous>)
at e.invokeClientMethod (http://192.168.0.30:5000/_framework/blazor.server.js:1:20022)
at e.processIncomingData (http://192.168.0.30:5000/_framework/blazor.server.js:1:18006)
Just want to say, this library was really helpful to me today. Is there a possibility it would support a folder select option? Right now, it defaults to the downloads directory.
Hi
I am unable to download an exe file I get "error reload the page" yet I am able to download exe files from websites so I do not think it is a system or browser settings issue.
I am able to download msi files .
Thanks
Laz
Currently only this exists
public ValueTask<DowloadFileResult> DownloadBinaryBuffers(string fileName, string contentType = "application/octet-stream")
The same will also need to be applied to the other direct Download####
calls as well
I'm not sure where the fault lies here is the server side code
return File(fileContents: excelFile, contentType: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
excelFile is a byte array produced using the EPPlus.
Then client side :
byte[] bytes = await httpResponseMessage.Content.ReadAsByteArrayAsync(); await BlazorDownloadFileService.DownloadFile("test555.xlsx", bytes);
The file that is "downloaded" in the browser is corrupt and won't open in Excel. The file that is downloaded is twice as big as the proper/working version of the file that I save server side using EPPlus.
First of all, thanks for making this library. I still see a JS dependency in version 1.0.10 that is on nuget.
When I use this code:
using var stream = new MemoryStream();
wb.Save(stream, SaveOptions.XlsxDefault); //An Excel spreadsheet
BlazorDownloadFileService.DownloadFile("SaviAnalysis-PROCs.xlsx", stream, "application/octet-stream");
It does not work and seems to treat the stream as empty. However, this code does work:
using var stream = new MemoryStream();
wb.Save(stream, SaveOptions.XlsxDefault);
BlazorDownloadFileService.DownloadFile("SaviAnalysis-PROCs.xlsx", stream.ToArray(), "application/octet-stream");
Unable to download a file larger than 500 MB.
Used your library, in particular the method: DownloadFile(string filename, Stream stream, CancellationToken cancellationTokenBytesRead, CancellationToken cancellationTokenJavaScriptInterop, string ContentType = "application/octet-stream").
But when you try to download a file larger than 200 MB, an exception occurs, which says that there was not enough memory. Stream was too long
Question on Stackoverflow: https://stackoverflow.com/questions/72356882/generate-a-link-to-the-download-file
Hello,
iam new to blazor and want to use this package to easily download files. It seems this package makes life easier. Thank you.
In my case i have minimal binary files in sql.
Is it posisble to use this memorystream of blob data for direct download? I noticed in examples all downloads are based on base64 data of binary file. I want to skip a base64 conversion.
Would it be possible to use something like this?
mymemorystream =direct_blobdata_fromsql;
<button @OnClick=@(async () => await BlazorDownloadFileService.DownloadFile("nyan.gif", mymemorystream, BufferSize, "application/octet-stream"))>Dowload Buffered Nyan From Stream
Thank you,
Hans
I'm passing a "Known" fully encoded URI filename into the DownloadFile
API, and then testing for it's existence, but nothing is being returned to say that a fialure has occurred. (Exception etc.
Currently this should fail because it is being used in a Mocked JSInterop environment (BUnit) and therefore no js script function should succeed.
Is it possible that you extend the DownloadFile methods with IProgress<double> progress? = null
which reports the % done on the download?
I need a singleton instance of BlazorDownloadFileService
(internal
class).
Please allow configuring registering IBlazorDownloadFileService
.
Expected api:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddBlazorDownloadFile(Microsoft.Extensions.DependencyInjection.ServiceLifetime lifetime);
}
}
EF.Core allows configuring DbContext
lifetime.
public static IServiceCollection Microsoft.Extensions.DependencyInjection.EntityFrameworkServiceCollectionExtensions.AddDbContext<TContext>(
[NotNull] this IServiceCollection serviceCollection,
[CanBeNull] Action<DbContextOptionsBuilder> optionsAction = null,
ServiceLifetime contextLifetime = ServiceLifetime.Scoped,
ServiceLifetime optionsLifetime = ServiceLifetime.Scoped)
It appears that DowloadFileResult is missing an "n" => DownloadFileResult
Hello @arivera12, I'd recommend skipping .NET 5 and going straight to .NET 6 in this case as .NET 6 has significant improvements to the byte array interop mechanisms.
Blazor no longer Base64 encodes/decodes byte[]
, and also natively supports streaming via JSStreamReference
/DotNetStreamReference
. This'll provide SIGNIFICANT improvements to stability and performance over .NET 5 in this area.
Links:
Originally posted by @TanayParikh in #28 (comment)
Can you show an example of how to get the "Filename" from a <a>
element so that it can be passed into you code for the FileSave ?
e.g. something like
<a>
element with a dummy application/unknown
encoding that will probably force the SaveAs
to popupMaybe a useful link:
https://stackoverflow.com/questions/61802606/download-file-from-asp-net-core-api-from-blazor-client-application/61803104#61803104
Can "the buffer management" solution, also have a way of
As per a rework of the gist above ?
Originally posted by @Smurf-IV in #5 (comment)
Hi
I increased the buffer size and I am now able to download my 110MB exe file but it takes 2 minutes for the download dialog to appear. I am using Blazor server app.
Is there a way of speeding the download up?
Thanks
Originally posted by @ltemimi in #24 (comment)
Hi, can you please show me how to download excel or csv file from list of objects?
thanks a lot.
Each time you call URL.createObjectURL()
, a new object URL is created, even if you've already created one for the same object. Each of these must be released by calling URL.revokeObjectURL()
when you no longer need them.
Browsers will release object URLs automatically when the document is unloaded; however, for optimal performance and memory usage, if there are safe times when you can explicitly unload them, you should do so.
Reference links:
https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL
https://developer.mozilla.org/en-US/docs/Web/API/URL/revokeObjectURL
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.