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  }