KristofferStrube / Blazor.FileSystemAccess

A Blazor wrapper for the File System Access browser API.
https://kristofferstrube.github.io/Blazor.FileSystemAccess/
MIT License
327 stars 39 forks source link

Processing a file "uploaded" via <InputFile> #8

Closed JasonKoopmans closed 2 years ago

JasonKoopmans commented 2 years ago

Is it possible to save a file that's "uploaded" through the <InputFile /> component and wind up saving it through the FileAPI implemented in your library? I see references to Blob and File -- but it isn't clear if those are translatable to the IBrowserFile interface thats available when the onchange event fires on the InputFile.

Consider:

<InputFile   OnChange="@OnChange" />

Which is handled by:

public void OnChange(InputFileChangeEventArgs args) {
  // file implements IBrowserFile, which contains OpenReadStream()
  var file = args.File

}
KristofferStrube commented 2 years ago

Thanks for the issue. I will try to explore this issue more closely.

My first bet would be that it is not directly possible, but we could probably read the content of the file as a string or byte array and then create a new Blob using some custom JSInterop and parse that to the library through the CreateAsync method on the Blob class.

KristofferStrube commented 2 years ago

I have now constructed an example that shows how to use them together.

@page "/InputFileExample"
@inject FileSystemAccessService FileSystemAccessService

<PageTitle>InputFile Example</PageTitle>

<InputFile OnChange="OnChange" />
<br />
@if (buffer is not null)
{
    <button @onclick="Save">Save</button>
}

@code {
    public byte[] buffer;

    public async Task OnChange(InputFileChangeEventArgs args)
    {
        var file = args.File;
        using var stream = file.OpenReadStream();
        buffer = new byte[stream.Length];
        await stream.ReadAsync(buffer);
    }

    public async Task Save()
    {
        var fileHandle = await FileSystemAccessService.ShowSaveFilePickerAsync();
        var writable = await fileHandle.CreateWritableAsync();
        await writable.WriteAsync(BytesToString(buffer));
        await writable.CloseAsync();
    }

    private static string BytesToString(byte[] bytes)
    {
        using MemoryStream stream = new MemoryStream(bytes);
        using StreamReader streamReader = new StreamReader(stream);
        return streamReader.ReadToEnd();
    }
}

The example can also be seen here: https://kristofferstrube.github.io/Blazor.FileSystemAccess/InputFileExample

It is not the most beautiful thing and I think a way that could makes this nicer would be if the FileSystemWritableFileStream where to extend the Stream class and override some of its methods like others have suggested earlier. I will keep this issue open to track progress on that.

JasonKoopmans commented 2 years ago

Thanks for sharing this. FileSystemWritableFileStream extending Stream would would be convenient. This example works for small files, but this needs to load the files completely into memory which would be a problem for larger files