Vue / Javascript 解析二进制图像文件错误?

用 tauri + Vue3 + markdown-it 构建的 markdown 解析工具。其中 template 的内容使用 v-html 绑定,所以解析出的图片是相对路径。

使用 npm run tauri dev 时顺利渲染,但是在 npm run tauri build 后,由于安全设置的问题,前端无法读取图片,此时路径依然显示相对路径 ../img/xxx.png,实际为 https://tauri.localhost/img/xxx.png

按照 convertFileSrc 案例尝试修改 tauri.conf.json,将 tauri.security.csp 设为 "csp": "default-src 'self'; img-src '*' asset: https://asset.localhost",其中 img-src 按照建议修改为 "*",另外还有一些尝试,都无法直接由前端 src 中的绝对路径来显示图片,所以考虑在检测到 md 文件渲染完毕后由 tauri/api/fs 直接读取本地文件或在 Rust 后端中将二进制文件信息传到前端,用 blob 解析。

其中,在 Rust 后端读取文件的方法试过,直到 blob 解析都没有报错,但不能正常显示图片,不知道是不是数据格式问题。而直接由前端调用 fs 读取 也出现了相似的问题,在最后一步无法显示图片。 由于后续测试过程中基本没有用到后端,预估是 Vue 或者图片文件解析的问题。

首先将 tauri.conf.json 中的 tauri.allowlist.fs 设为:

    "fs": {
        "all": true,
        "scope": ["D:/**", "E:/**"]
    }

即允许 fs 读取两个盘中所有文件;

将上面提到的 csp 设为: "csp": "asset:"

前端对图片的处理主要是:

let img_path = "E:/myProjects2/tauri_vue/mdren/img/a-1-01.png";
const contents = await readBinaryFile(img_path);  // contents 为 Uint8Array,须转换为 string
const str_contents = () => {
  let binary = "";
  let len = contents.byteLength;
  for (let i = 0; i < len; i++) {
    binary += String.fromCharCode(bytes[i]);
  }
  return binary;
}
let src = window.btoa(str_contents);  // window.btoa 处理字符串格式的二进制图片数据

// ...

// 上面已经将 markdown 渲染到了 html 中,
// 检测到 DOM 变化后调用 reloadImg,将里面的 <img id="img-to-remove" ...> 重新渲染
async function reloadImg() {
  console.log("function called");
  let img_element = document.querySelectorAll("#img-to-remove");
  console.log(img_element);
  img_element.forEach(async (element) => {
    element.src = "data:image/png;base64," + src;
  });
}

在软件中按 F12 调出开发者工具后,可以看到对应图片的 src 是正常的,但是图片不确定是否有编码等错误,表现为损坏或未正常加载的形式。

结果如图


重新做了个小 demo,未用到 Rust 后端,前端代码如下:

components/imgDecode.vue

<script setup>
import { nextTick, ref } from "vue";
import markdownIt from "markdown-it";
import { invoke, convertFileSrc } from '@tauri-apps/api/tauri';
import { resolve, resourceDir } from '@tauri-apps/api/path';
import { readBinaryFile } from "@tauri-apps/api/fs";

const md = markdownIt();
md.set({ html: true });
const md_context = ref();

// 任选一张图片为例
let img_path = "E:/myProjects2/tauri_vue/mdren/img/a-1-01.png";
const contents = await readBinaryFile(img_path);
const str_contents = () => {
  let binary = "";
  let len = contents.byteLength;
  for (let i = 0; i < len; i++) {
    binary += String.fromCharCode(bytes[i]);
  }
  return binary;
}
let src = window.btoa(str_contents);

currentLogics();

async function currentLogics() {
  try {
    // 先解析 markdown 文件
    await solveMd();
    // 重新渲染
    nextTick(() => {
      reloadImg();
    })
  } catch (e) {
    console.log(e);
  }
}

async function solveMd() {
  md_context.value = await md.render("$\\xRightarrow{aaaaaaaaaaaaaaaaaaa}$\n\n<img id='img-to-remove' src='../img/a-1-01.png'>");
}

async function reloadImg() {
  console.log("function called");
  let img_element = document.querySelectorAll("#img-to-remove");
  console.log(img_element);
  img_element.forEach(async (element) => {
    element.src = "data:image/png;base64," + src;
  });
}
</script>

<template>
    <div v-html="md_context"></div>
</template>

<style scoped>

</style>

App.vue

<script setup>
import imgDecode from "./components/imgDecode.vue";
</script>

<template>
    <Suspense>
        <resolveData />
    </Suspense>
</template>

<style scoped>

</style>
阅读 1.3k
1 个回答

解决了,不用 window.btoa,还用 blob 就行:

let img_path = "E:/myProjects2/tauri_vue/mdren/img/a-1-01.png";
const contents = await readBinaryFile(img_path);
let blob = new Blob([contents], { type: "image/png" });

async function reloadImg() {
  console.log("function called");
  let img_element = document.querySelectorAll("#img-to-remove");
  console.log(img_element);
  img_element.forEach(async (element) => {
    element.src = URL.createObjectURL(blob);
  });
}

之前用 blob 不行应该是 csp 忘了改或者 Rust 后端传过来的数据有问题。

在软件里再试一次,希望能过。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题