github.com/0chain/gosdk@v1.17.11/wasmsdk/jsbridge/file_writer.go (about) 1 //go:build js && wasm 2 // +build js,wasm 3 4 package jsbridge 5 6 import ( 7 "errors" 8 "io" 9 "io/fs" 10 "syscall/js" 11 ) 12 13 type FileWriter struct { 14 writableStream js.Value 15 uint8Array js.Value 16 fileHandle js.Value 17 bufLen int 18 buf []byte 19 bufWriteOffset int 20 writeError bool 21 } 22 23 const writeBlocks = 10 24 25 // len(p) will always be <= 64KB 26 func (w *FileWriter) Write(p []byte) (int, error) { 27 //init buffer if not initialized 28 if len(w.buf) == 0 { 29 w.buf = make([]byte, len(p)*writeBlocks) 30 } 31 32 //copy bytes to buf 33 if w.bufWriteOffset+len(p) > len(w.buf) { 34 w.writeError = true 35 return 0, io.ErrShortWrite 36 } 37 n := copy(w.buf[w.bufWriteOffset:], p) 38 w.bufWriteOffset += n 39 if w.bufWriteOffset == len(w.buf) { 40 //write to file 41 if w.bufLen != len(w.buf) { 42 w.bufLen = len(w.buf) 43 w.uint8Array = js.Global().Get("Uint8Array").New(w.bufLen) 44 } 45 js.CopyBytesToJS(w.uint8Array, w.buf) 46 _, err := Await(w.writableStream.Call("write", w.uint8Array)) 47 if len(err) > 0 && !err[0].IsNull() { 48 w.writeError = true 49 return 0, errors.New("file_writer: " + err[0].String()) 50 } 51 //reset buffer 52 w.bufWriteOffset = 0 53 } 54 return len(p), nil 55 } 56 57 // func (w *FileWriter) WriteAt(p []byte, offset int64) (int, error) { 58 // uint8Array := js.Global().Get("Uint8Array").New(len(p)) 59 // js.CopyBytesToJS(uint8Array, p) 60 // options := js.Global().Get("Object").New() 61 // options.Set("type", "write") 62 // options.Set("position", offset) 63 // options.Set("data", uint8Array) 64 // options.Set("size", len(p)) 65 // _, err := Await(w.writableStream.Call("write", options)) 66 // if len(err) > 0 && !err[0].IsNull() { 67 // return 0, errors.New("file_writer: " + err[0].String()) 68 // } 69 // return len(p), nil 70 // } 71 72 func (w *FileWriter) Close() error { 73 74 if w.bufWriteOffset > 0 && !w.writeError { 75 w.buf = w.buf[:w.bufWriteOffset] 76 uint8Array := js.Global().Get("Uint8Array").New(len(w.buf)) 77 js.CopyBytesToJS(uint8Array, w.buf) 78 _, err := Await(w.writableStream.Call("write", uint8Array)) 79 if len(err) > 0 && !err[0].IsNull() { 80 return errors.New("file_writer: " + err[0].String()) 81 } 82 } 83 84 _, err := Await(w.writableStream.Call("close")) 85 if len(err) > 0 && !err[0].IsNull() { 86 return errors.New("file_writer: " + err[0].String()) 87 } 88 return nil 89 } 90 91 func (w *FileWriter) Read(p []byte) (int, error) { 92 return 0, errors.New("file_writer: not supported") 93 } 94 95 func (w *FileWriter) Seek(offset int64, whence int) (int64, error) { 96 return 0, nil 97 } 98 99 func (w *FileWriter) Sync() error { 100 return nil 101 } 102 103 func (w *FileWriter) Stat() (fs.FileInfo, error) { 104 return nil, nil 105 } 106 107 func NewFileWriter(filename string) (*FileWriter, error) { 108 109 if !js.Global().Get("window").Get("showSaveFilePicker").Truthy() || !js.Global().Get("window").Get("WritableStream").Truthy() { 110 return nil, errors.New("file_writer: not supported") 111 } 112 113 showSaveFilePicker := js.Global().Get("window").Get("showSaveFilePicker") 114 //create options with suggested name 115 options := js.Global().Get("Object").New() 116 options.Set("suggestedName", filename) 117 118 //request a file handle 119 fileHandle, err := Await(showSaveFilePicker.Invoke(options)) 120 if len(err) > 0 && !err[0].IsNull() { 121 return nil, errors.New("file_writer: " + err[0].String()) 122 } 123 //create a writable stream 124 writableStream, err := Await(fileHandle[0].Call("createWritable")) 125 if len(err) > 0 && !err[0].IsNull() { 126 return nil, errors.New("file_writer: " + err[0].String()) 127 } 128 return &FileWriter{ 129 writableStream: writableStream[0], 130 fileHandle: fileHandle[0], 131 }, nil 132 } 133 134 func NewFileWriterFromHandle(dirHandler js.Value, name string) (*FileWriter, error) { 135 options := js.Global().Get("Object").New() 136 options.Set("create", true) 137 fileHandler, err := Await(dirHandler.Call("getFileHandle", name, options)) 138 if len(err) > 0 && !err[0].IsNull() { 139 return nil, errors.New("dir_picker: " + err[0].String()) 140 } 141 142 writableStream, err := Await(fileHandler[0].Call("createWritable")) 143 if len(err) > 0 && !err[0].IsNull() { 144 return nil, errors.New("file_writer: " + err[0].String()) 145 } 146 return &FileWriter{ 147 writableStream: writableStream[0], 148 fileHandle: fileHandler[0], 149 }, nil 150 }