浏览器 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,接收成功即可在本地进行预览操作