-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Add storage API to open a file for appending, without truncating it's content first #5176
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
implemented in both memory and desktop, need to implement for mobile still
because I forgot how io reader works
andydotxyz
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clear and clean, thanks for looking at this - a good addition.
I think it will need the mobile code added to land though.
Would tips on how to progress be sufficient, or are you handing this on to someone else?
|
Thank you that was super encouraging and got me the drive to try the mobile code myself! Let me know if this mobile code makes sense to you, and if someone with an android or ios setup - well - set up could check that this code works I'd super appreciate it as well! |
thanks to https://lists.apple.com/archives/cocoa-dev/2002/Aug/msg00754.html for giving me the necessary shape for the function
|
Looking great. The code compiles and runs on my iOS test devices. Thanks! |
|
I have discovered in writing this test snippet that I in fact forgot to implement the last part of the puzzle, the storage interface itself. Fixed and pushed, and here's the snippet I used to check it on my PC func main() {
app := app.NewWithID("io.fyne.appendtest")
win := app.NewWindow("Append Test")
win.Resize(fyne.NewSize(400, 300))
app.Storage().Create("test.txt")
btnAppend := widget.NewButton("append to file", func() {
doc, err := app.Storage().Append("test.txt")
if err != nil {
dialog.ShowError(err, win)
return
}
_, err = doc.Write([]byte("test line\n"))
if err != nil {
dialog.ShowError(err, win)
return
}
err = doc.Close()
if err != nil {
dialog.ShowError(err, win)
return
}
dialog.ShowInformation("Success", "Wrote to file", win)
})
btnRead := widget.NewButton("read file", func() {
doc, err := app.Storage().Open("test.txt")
if err != nil {
dialog.ShowError(err, win)
return
}
b := make([]byte, 64)
_, err = doc.Read(b)
if err != nil {
dialog.ShowError(err, win)
return
}
dialog.ShowInformation("Success", "Successfully read from file\n\ncontents:\n"+string(b), win)
println(string(b))
})
content := container.NewGridWithRows(2, btnAppend, btnRead)
win.SetContent(container.NewCenter(content))
win.ShowAndRun()
} |
1ad36f8 to
eeb8511
Compare
|
the new "it works on my system" with the storage interface reverted to how it was is: func main() {
app := app.NewWithID("io.fyne.appendtest")
win := app.NewWindow("Append Test")
win.Resize(fyne.NewSize(400, 300))
root := app.Storage().RootURI()
uri, err := storage.Child(root, "test.txt")
if err != nil {
dialog.ShowError(err, win)
return
}
btnAppend := widget.NewButton("append to file", func() {
doc, err := storage.Appender(uri)
defer doc.Close()
if err != nil {
dialog.ShowError(err, win)
return
}
_, err = doc.Write([]byte("test line\n"))
if err != nil {
dialog.ShowError(err, win)
return
}
dialog.ShowInformation("Success", "Wrote to file", win)
})
btnRead := widget.NewButton("read file", func() {
doc, err := storage.Reader(uri)
defer doc.Close()
if err != nil {
dialog.ShowError(err, win)
return
}
b := make([]byte, 64)
_, err = doc.Read(b)
if err != nil {
dialog.ShowError(err, win)
return
}
dialog.ShowInformation("Success", "Successfully read from file\n\ncontents:\n"+string(b), win)
println(string(b))
})
content := container.NewGridWithRows(2, btnAppend, btnRead)
win.SetContent(container.NewCenter(content))
win.ShowAndRun()
} |
internal/driver/mobile/repository.go
Outdated
| return fileWriterForURI(u, true) | ||
| } | ||
|
|
||
| // TODO: need someone who understands native code to write this, I'm not sure |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like this TODO could go?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OOPS! deleted
andydotxyz
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for working on this. I think there is still a breaking change in the Repository interface API, but we have discussed how to resolve it. I left notes here as well which I hope help.
storage/repository/repository.go
Outdated
| // file if it exists | ||
| // | ||
| // Since: 2.6 | ||
| Appender(u fyne.URI) (fyne.URIWriteCloser, error) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the remaining breaking change where a new (optional) interface will be needed, perhaps AppendableRepository?
storage/uri.go
Outdated
| return nil, err | ||
| } | ||
|
|
||
| wrepo, ok := repo.(repository.WritableRepository) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corresponding to the note above this should be a check for supporting the Appender
|
This doesn't work work on Android - I finally tracked it down to the This patch fixes Android! diff --git a/internal/driver/mobile/android.c b/internal/driver/mobile/android.c
index 14e72b66b..db4f55c06 100644
--- a/internal/driver/mobile/android.c
+++ b/internal/driver/mobile/android.c
@@ -154,9 +154,9 @@ void* saveStream(uintptr_t jni_env, uintptr_t ctx, char* uriCstr, bool truncate)
jobject uri = parseURI(jni_env, ctx, uriCstr);
jstring modes = NULL;
if (truncate) {
- jstring modes = (*env)->NewStringUTF(env, "wt"); // truncate before write
+ modes = (*env)->NewStringUTF(env, "wt"); // truncate before write
} else {
- jstring modes = (*env)->NewStringUTF(env, "wa");
+ modes = (*env)->NewStringUTF(env, "wa");
}
jobject stream = (jobject)(*env)->CallObjectMethod(env, resolver, saveOutputStream, uri, modes);
jthrowable loadErr = (*env)->ExceptionOccurred(env); |
|
Unfortunately on iOS it reports writing successfully but the read back just gets EOF not the file content |
|
The following change made it work on iOS: diff --git a/internal/driver/mobile/file_ios.m b/internal/driver/mobile/file_ios.m
index 18b08de24..9ff83770d 100644
--- a/internal/driver/mobile/file_ios.m
+++ b/internal/driver/mobile/file_ios.m
@@ -39,7 +39,7 @@ bool iosExistsPath(const char* path) {
NSFileHandle* handle = [NSFileHandle fileHandleForWritingToURL:url error:&err];
if (!truncate) {
- [handle truncateFileAtOffset:[handle seekToEndOfFile]];
+ [handle seekToEndOfFile];
}
return handle;but in part it was because the read operation can return EOF error which we were treating as a problem, so try this: _, err = doc.Read(b)
if err != nil && err != io.EOF {
dialog.ShowError(err, win)
return
} |
andydotxyz
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking great. With those small changes for Android and iOS I think this is a winner
andydotxyz
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks so much for this
Implemented in both memory and desktop, need to implement for mobile still, setting this as a draft for now, I don't know who can do mobile and how but for now this is what I could pull off.
Description:
Needed an option to add to a file in the storage without truncating it first, specifically to write long lasting log files.
Fixes #(issue)
Checklist:
Where applicable: