Cannot write a file
System.MissingMethodException: Method not found: void KristofferStrube.Blazor.Streams.WritableStream..ctor(Microsoft.JSInterop.IJSRuntime,Microsoft.JSInterop.IJSObjectReference) at KristofferStrube.Blazor.FileSystem.FileSystemWritableFileStream.CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference) at KristofferStrube.Blazor.FileSystem.FileSystemFileHandle.CreateWritableAsync(FileSystemCreateWritableOptions fileSystemCreateWritableOptions)
I "can" read files, so I assume I have setup all the basics properly. just the writing is a problem when it gets to the "CreateWriteableAsync"
sample below...
` async public Task<FileSystemFileHandle?> SaveFilePicker(WellKnownDirectory? _StartIn = null) { try {
var options = new SaveFilePickerOptionsStartInWellKnownDirectory {
Types = [
new FilePickerAcceptType {
Description = "json Files",
Accept = new() { { "json/*", value } }
}
],
StartIn = _StartIn,
};
return await FileSystem.ShowSaveFilePickerAsync(options);
} catch (System.Runtime.InteropServices.JavaScript.JSException ex) {
Console.WriteLine(ex.Message);
} catch (Exception ex) {
Console.WriteLine(ex.Message);
}
return null;
}
async void ExportTest(){
FileSystemFileHandle? h = await SaveFilePicker();
if (h != null) {
var writable = await h.CreateWritableAsync();
await writable.WriteAsync("a quick test");
await writable.CloseAsync();
}
}
}
`
Hey @HarvieKrumpet thanks for the interest in my libraries. 😃
I think the problem is that you have selected specific versions of either the Blazor.FileAPI or Blazor.Streams packages which are not yet supported with this library.
I’m currently working on updating Blazor.FileSystem first and after that Blazor.FileSystemAccess so that you can use all the newest bits introduced in their new versions. Until then you will have to remove explicit references to the Blazor.FileAPI, Blazor.Streams, Blazor.DOM, Blazor.WebIDL NuGet packages to get it to use the versions that are compatible.
Sorry for the inconvenience. A lot of people have been requesting that these packages should be updated recently so I will prioritize it. Could you tell me anything about what project you are working on? Then I might be able to ensure that I handle that scenario well while updating the package.
I can limp by, by just utlizing the typical browser assisted download files approach for saves. the important one which is open/read file picking and actually reading files anywhere you have provided which works and when used in conjunction with indexeddb as main storage, blazor wasm turns out to very capable and convenient all in the browser c# application platform. A general purpose single step save makes it convenient enough to port all my c# apps into blazor even if they were purely desktop (wpf) versions in stand alone web app mode. blazor/indexeddb/filesystem/right click context menu could replace all those other cross platform technologies. So I have multiple projects all quite different from each other. but a convenient "normal" file save is all that is missing now from blazor.
I have the same problem! Hope the problem canbe resolved very soon. Thanks
Update: I have a PR for Blazor.FileSystem which I'm currently self-reviewing: https://github.com/KristofferStrube/Blazor.FileSystem/pull/10
After that, I will start upgrading Blazor.FileSystemAccess as well, which should be faster due to the smaller API surface.
@qunshen, is your use case similar, or do you have some other scenario that this library supports? :) I would love to hear about it to ensure that the project and the upgrade cater to those needs.
@qunshen @HarvieKrumpet I have now released version 0.4.0-alpha.2 of Blazor.FileSystem, which should be compatible with the current version of Blazor.FileSystemAccess, so if you take a dependency on that, then you can enjoy all the new features of Blazor.WebIDL, Blazor.DOM, Blazor.Streams, Blazor.FileAPI, and of course, Blazor.FileSystem itself.
I'm still working on updating this library so that you won't need to make that dependency directly, but I would appreciate it if you could try it out to see if it works for your use cases. Once I'm done with upgrading Blazor.FileSystemAccess I will make a non-alpha release of Blazor.FileSystem as well, but I wanted it out early so that you and I could test it quickly.
Works! thank you for fixing this. Thought I would contribute a robust all in one wrapper around your filesystem calls. It exposes all the features you have put in it. and how to call it. It wraps the handle into a FileInfo ` public class FileInfo { public ulong Size; public FileSystemFileHandle? h; public KristofferStrube.Blazor.FileAPI.File? file; public string? Name; public FileSystemHandleKind? Kind; public DateTime UTCModified; public PermissionState p; public string? Mime;
// Private constructor to prevent direct instantiation
private FileInfo(FileSystemFileHandle _h) {
h = _h;
}
// Async factory method
public static async Task<FileInfo> CreateAsync(FileSystemFileHandle _h, FileSystemPermissionMode _State) {
var fileInfo = new FileInfo(_h);
await fileInfo.FillFileInfoAsync(_State);
return fileInfo;
}
private async Task FillFileInfoAsync(FileSystemPermissionMode _State) {
if (h != null) {
Name = await h.GetNameAsync();
Kind = await h.GetKindAsync();
if (_State == FileSystemPermissionMode.Read) {
file = await h.GetFileAsync();
UTCModified = await file.GetLastModifiedAsync();
Size = await file.GetSizeAsync();
Mime = await file.GetTypeAsync();
} else {
UTCModified = DateTime.UtcNow;
}
p = await h.QueryPermissionAsync(new() { Mode = _State });
}
}
public async Task<T> ReadAsync<T>() where T : class, new() {
if (file == null) throw new Exception("No File Handle");
string json = await file.TextAsync();
return Json.FromString<T>(json, Json.Tight);
}
public async Task<T> ReadAsyncSimple<T>() where T : class {
if (file == null) throw new Exception("No File Handle");
if (typeof(T) == typeof(string)) return (T)(object)await file.TextAsync();
if (typeof(T) == typeof(byte[])) return (T)(object)await file.ArrayBufferAsync();
if (typeof(T) == typeof(ReadableStream)) return (T)(object)await file.StreamAsync();
throw new NotSupportedException($"Type {typeof(T).Name} is not supported for JSON deserialization.");
}
public async Task WriteAsync<T>(T data) where T : class {
if (h == null) {
return;
}
try {
FileSystemWritableFileStream? writable = await h.CreateWritableAsync();
if (writable == null)
return;
switch (data) {
case string s:
await writable.WriteAsync(s);
break;
case byte[] byteArray:
await writable.WriteAsync(byteArray);
break;
case Blob blob:
await writable.WriteAsync(blob);
break;
case BlobWriteParams blobParams:
await writable.WriteAsync(blobParams);
break;
case StringWriteParams stringParams:
await writable.WriteAsync(stringParams);
break;
case ByteArrayWriteParams byteArrayParams:
await writable.WriteAsync(byteArrayParams);
break;
default:
throw new NotSupportedException($"Type {typeof(T)} is not supported for WriteAsync.");
}
await writable.CloseAsync();
} catch (Exception e) {
Debug.WriteLine(e.Message);
throw new Exception($"Failed to WriteAsync {e.Message}");
}
}
}
}
/// Example use public async void ExportChat(ChatHistory? _ch) { if (_ch != null) { string? json = Json.ToString(_ch, Json.Pretty); // ordinary serializer in a wrapper
if (json != null) {
FilePickerAcceptType JsonType = new() {
Description = "json Files",
Accept = new() { { "json/*", [".json", ".js"] } }
};
try {
FileSystemFileHandle? h = await SaveFilePicker(JsonType);
if (h != null) {
FileInfo fi = await FileInfo.CreateAsync(h, FileSystemPermissionMode.ReadWrite);
await fi.WriteAsync(json);
}
} catch (JsonException ex) {
statusBar.SetServiceStatus($"Error {ex.Message}");
}
}
}
async public Task ImportChat() {
FileInfo? ImportedDB = await AeonSVC.OpenFilePicker([JsonType]);
if (ImportedDB != null) {
ChatDB? db = await ImportedDB.ReadAsync<ChatDB>(); // ExportDB is 'any class you feed it'
/// do whatever you want with your received file
}
}
`
the rest of fileinfo.... ` static async public Task<FileInfo?> OpenFilePicker(FilePickerAcceptType[] _Types, WellKnownDirectory? _StartIn = null) { try { var options = new OpenFilePickerOptionsStartInWellKnownDirectory { Types = _Types, Multiple = false, StartIn = _StartIn };
IFileSystemAccessService? FileSystem = (Program.StaticServiceProviderHook?.GetService<IFileSystemAccessService>()) ?? throw new Exception("NO IFileSystemAccessService");// Get the service instance and invoke the event
var fileHandles = await FileSystem.ShowOpenFilePickerAsync(options);
FileSystemFileHandle h = fileHandles.Single();
FileInfo fi = await FileInfo.CreateAsync(h, FileSystemPermissionMode.Read);
return fi;
} catch (System.Runtime.InteropServices.JavaScript.JSException) {
}
return null;
}
async public Task<FileSystemFileHandle?> SaveFilePicker(FilePickerAcceptType _Type, WellKnownDirectory? _StartIn = null) {
try {
var options = new SaveFilePickerOptionsStartInWellKnownDirectory {
Types = [_Type], StartIn = _StartIn,
};
IFileSystemAccessService? FileSystem = (Program.StaticServiceProviderHook?.GetService<IFileSystemAccessService>()) ?? throw new Exception("NO IFileSystemAccessService");// Get the service instance and invoke the event
return await FileSystem.ShowSaveFilePickerAsync(options);
} catch (System.Runtime.InteropServices.JavaScript.JSException ex) {
statusBar.SetServiceStatus(ex.Message);
} catch (Exception ex) {
statusBar.SetServiceStatus(ex.Message);
}
return null;
}
`