Aspnetcore: Reading local files

Created on 21 Apr 2018  路  5Comments  路  Source: dotnet/aspnetcore

Blazor newbie here.

I'm trying to read files locally (browser-side) and do something with them in C#.

I'm using the <input type="file" onchange="@OnChange" /> tag. In my OnChange method I can only receive the filename, but cannot read it:

    void OnChange(UIEventArgs eventArgs) {
        var changeEventArgs = (UIChangeEventArgs)eventArgs;
        var filepath = changeEventArgs.Value.ToString();  // returns C:\fakepath\test.txt
        data = System.IO.File.ReadAllText(filepath);   // System.IO.FileNotFoundException: Could not find file "/C:\fakepath\test.txt"
    }

How can I do this? An alternative would be to implement a javascript handler, then use HTML5 File API to read the file, then pass the raw bytes over to a C# method via custom C# binding. Would that be the way to go, or should this be easier?

area-blazor

Most helpful comment

In your Blazor.platform.callMethod call, you're choosing to send the data as a string. It doesn't have to be a string. You can work with arbitrary data types. For example, you could first call a method that returns an empty byte array of a given length, and then your JS-side code could copy the binary data into it, and then you could call your MyMethod with the arg type being a byte[].

I recognize that this is nonobvious and doing it is quite advanced. We don't have any docs about this level of JS interop yet (the closest being some combination of the Emscripten and Mono docs). So for now unless this is really damaging the responsiveness of your app, I think your existing base64 solution is probably good to stick with.

However, I'd much rather directly use C# System.IO.File apis to read files

This is something we've seriously considered. The tricky bit is that unless absolutely 100% of the System.IO.File APIs can map to exactly equivalent JS APIs, then it's just a recipe for obscure bugs. For example, if some third-party NuGet package tries to do things with System.IO.File but the runtime doesn't exactly simulate what happens on the desktop, then it would fail and developers would have no obvious way to make sense of why. It's much safer to say that web browsers don't have a traditional filesystem, but rather there are some different HTML5 "file" APIs you can use. Then developers will have to account for those differences in their designs.

All 5 comments

I managed to get this working, but it's a major hack: Read the file in javascript, convert to base64 and pass it over to C#.

 <input type="file" onchange="handleFileSelect(this.files);" />
    function handleFileSelect(files) {
        var file = files[0];
        var reader = new FileReader();

        reader.onloadend = function (evt) {
            if (evt.target.readyState == FileReader.DONE) { // DONE == 2
                let messageAsDotNetString = Blazor.platform.toDotNetString(arrayBufferToBase64(evt.target.result));

                const decompileMethod = Blazor.platform.findMethod(
                    'Client',   // Assembly name
                    'Client',   // Namespace
                    'MyClass',            // Class name
                    'MyMethod'      // Method name
                );

                Blazor.platform.callMethod(
                    decompileMethod,         // Method handle
                    null,                       // Target
                    [messageAsDotNetString]   // Arguments
                );
            }
        };
        var blob = file.slice(0, file.size);
        reader.readAsArrayBuffer(blob);
    }
    public static class MyClass {
        public static void MyMethod(string base64) {
            byte[] bytes = Convert.FromBase64String(base64);
            using (var sr = new MemoryStream(bytes)) {
                // do stuff here
            }
        }
    }

Obviously this isn't optimal: If I was able to pass not only strings, but also blobs, to C#, that would be an improvement. However, I'd much rather directly use C# System.IO.File apis to read files. Is there any way to do this yet?

In your Blazor.platform.callMethod call, you're choosing to send the data as a string. It doesn't have to be a string. You can work with arbitrary data types. For example, you could first call a method that returns an empty byte array of a given length, and then your JS-side code could copy the binary data into it, and then you could call your MyMethod with the arg type being a byte[].

I recognize that this is nonobvious and doing it is quite advanced. We don't have any docs about this level of JS interop yet (the closest being some combination of the Emscripten and Mono docs). So for now unless this is really damaging the responsiveness of your app, I think your existing base64 solution is probably good to stick with.

However, I'd much rather directly use C# System.IO.File apis to read files

This is something we've seriously considered. The tricky bit is that unless absolutely 100% of the System.IO.File APIs can map to exactly equivalent JS APIs, then it's just a recipe for obscure bugs. For example, if some third-party NuGet package tries to do things with System.IO.File but the runtime doesn't exactly simulate what happens on the desktop, then it would fail and developers would have no obvious way to make sense of why. It's much safer to say that web browsers don't have a traditional filesystem, but rather there are some different HTML5 "file" APIs you can use. Then developers will have to account for those differences in their designs.

@discostu105 : Since 0.4.0, its possible to skip the base64 step as Blazor.platform.toUint8Array was introduced. I made an example here.

@Tewr Great. Thanks for getting back to me. I'll give it a try :).

@discostu105 Please take a look at the following video where I demo a Blazor interop for handling files. https://youtu.be/-IuZQeZ10Uw

Here's the package I used: https://www.nuget.org/packages/Tewr.Blazor.FileReader/

Code: https://github.com/EdCharbeneau/BlazorStateCognitive/blob/master/BlazorStateCognitive.Client/Pages/FetchData.cshtml

Was this page helpful?
0 / 5 - 0 ratings

Related issues

FourLeafClover picture FourLeafClover  路  3Comments

ermithun picture ermithun  路  3Comments

BrennanConroy picture BrennanConroy  路  3Comments

UweKeim picture UweKeim  路  3Comments

rbanks54 picture rbanks54  路  3Comments