浏览器 indexedb 保存文件如何导出到本地
背景:
Webassembly 可以把文件保存到indexed,而把文件导出到本地预览,或者可视化的查看文件内容,极其不方便, 本文就介绍我的具体实现方式
思路
我的想法是把文件通过网络发送的本机的一个服务器,然后服务器接收文件流,最后写成一个文件,保存在本地,在本地进行预览
实现过程
本来想用c网络编程里边的 socket,写了demo发现不行,后来查阅资料说的需要用websocket,下边就是具体相关代码
初始化WebSocket
JavaScript const socket = new WebSocket('ws://localhost:40001/ws_echo'); // Connection opened socket.addEventListener('open', (event) => { socket.send('Hello Server!'); }); // Listen for messages socket.addEventListener('message', (event) => { console.log('Message from server ', event.data); });
发送文件
JavaScript function updateLoad() { console.log("update load file"); let path = allocateUTF8("output.mp4") let buf_addr = Module._readFile(path); let size = Module._getFileSize(); var u8o = new Uint8ClampedArray(Module.HEAPU8.subarray(buf_addr, buf_addr + size)); socket.send(u8o); socket.send("OK"); }
可以看到有两个webassembly的方法_readFile和_getFileSize(),
JavaScript MDWord g_dwFileSize = 0; MByte *g_pFileData = 0; int EMSCRIPTEN_KEEPALIVE getFileSize() { return g_dwFileSize; } uint8_t* EMSCRIPTEN_KEEPALIVE readFile(const char *pFileName) { std::string filePath = std::move(std::string("/data/") + pFileName); QLOGE("readFile filePath=%s\n", filePath.c_str()); MDWord dwSize = MStreamFileGetSize(filePath.c_str()); QLOGE("readFile dwSize = %d\n", dwSize); if (dwSize != g_dwFileSize) { if (g_pFileData != MNull) { MMemFree(MNull, g_pFileData); } } g_pFileData = (MByte *)MMemAlloc(MNull, dwSize); g_dwFileSize = dwSize; FILE* fp = fopen(filePath.c_str(), "rb"); if (fp != MNull ) { char buf[1024] ={0}; int size = 0; dwSize = 0; while((size = fread(buf,1,sizeof(buf),fp)) > 0) { MMemCpy(g_pFileData + dwSize, buf, size); dwSize += size; } fclose(fp); } return g_pFileData; }
很简单的客户端
接收文件
接收文件需要在本地构建一个服务器,这里我们采用go语言实现了一个简单服务器,具体代码如下:
Go package main import ( "fmt" "log" "net/http" "syscall" "golang.org/x/net/websocket" "os" "os/signal" ) func main() { log.Println("ws_echo start...") wsPort := 40001 go func() { log.Println(fmt.Sprint("WebSocket:", wsPort, " Listening ...")) http.Handle("/ws_echo", websocket.Handler(webSocketHandler)) err := http.ListenAndServe(fmt.Sprint(":", wsPort), nil) if err != nil { panic("ListenAndServe: " + err.Error()) } }() httpPort := 80 go func() { log.Println(fmt.Sprint("http:", httpPort, " Listening ...")) err := http.ListenAndServe(fmt.Sprint(":", httpPort), http.FileServer(http.Dir("./"))) if err != nil { panic("ListenAndServe: " + err.Error()) } }() ch := make(chan os.Signal, 1) signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM) log.Printf("ws_echo quit (%v)\n", <-ch) } func webSocketHandler(ws *websocket.Conn) { ws.PayloadType = websocket.TextFrame defer ws.Close() rtemp := make([]byte, 1024*1024*50) file, err := os.OpenFile( "test.bin", os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0666, ) if err != nil { log.Fatal(err) } defer file.Close() ws.Read(rtemp) for { n, err := ws.Read(rtemp) if err != nil { log.Println("Error:Read:", err) return } // myString := string(rtemp) log.Println("size:", n) // log.Println("read:", myString) // n, err = ws.Write(rtemp[:n]) if n==2 && rtemp[0]=='O' && rtemp[1]=='K'{ log.Printf("Save Ok.\n") break; } // 写字节到文件中 byteSlice := rtemp[:n] bytesWritten, err := file.Write(byteSlice) if err != nil { log.Fatal(err) } log.Printf("Wrote %d bytes.\n", bytesWritten) } }
最终我们把接收到文件保存成了test.bin,接收成功即可在本地进行预览操作