pdf文件根据页数解析成图片 js vue3

pdf文件根据页数解析成图片 js vue3

安装pdfjs-dist

javascript 复制代码
npm install pdfjs-dist
javascript 复制代码
<template>
  <div>
    <div style="margin-top:1%">            
      <button @click="prevPage">上一页</button>
      <button @click="nextPage">下一页</button>
      <button @click="exportImages">导出图片</button>
      <button @click="choosePdf">选择一个pdf文件</button>
      <input 
        ref="fileInput"
        style="display:none" 
        type="file" 
        accept="application/pdf"
        @change="handleFileChange">
    </div>
    <div style="margin-top:1%;color:#000;">
      <span class="pdfInfos">页码:<span>{{ currentPage }}</span>/<span>{{ totalPages }}</span></span>
      <span class="pdfInfos">文件名:<span>{{ fileName }}</span></span>
      <span class="pdfInfos">文件大小:<span>{{ fileSize }}</span></span>
    </div>
    <div style="position: relative;">
      <div id="container">
        <canvas
          v-for="page in totalPages"
          :key="page"
          :id="`pageNum${page}`"
          :ref="el => setCanvasRef(el, page)"
          :style="{ display: page === currentPage ? 'block' : 'none', width: '100%', height: '100%' }"
        />
      </div>
      <img 
        id="imgloading" 
        style="position: absolute;top: 20%;left: 2%;" 
        :style="{ display: loading ? 'block' : 'none' }"
        src="loading.gif">
    </div>
  </div>
</template>

<script setup>
import { ref, reactive } from 'vue'
import * as pdfjsLib from "pdfjs-dist/build/pdf";
import "pdfjs-dist/build/pdf.worker.mjs";

// 响应式数据
const fileInput = ref(null)
const canvasRefs = reactive({})
const currentPage = ref(1)
const totalPages = ref(0)
const fileName = ref('')
const fileSize = ref('')
const loading = ref(false)
const scale = 2
let pdfDoc = null

const images = ref([])
// base64转file
const base64ToFile = (base64, filename = "") => {
  const arr = base64.split(",");
  let mime = arr[0].match(/:(.*?);/)[1]; // 匹配出图片类型
  mime = mime.replace("data:", ""); // 去掉data:image/png;base64 // 去掉url中的base64,并转化为Uint8Array类型
  const bstr = atob(arr[1]);
  let n = bstr.length;
  const u8arr = new Uint8Array(n);
  while (n--) {
    u8arr[n] = bstr.charCodeAt(n);
  }
  return new File([u8arr], filename, { type: mime });
};

// 设置canvas引用
const setCanvasRef = (el, page) => {
  if (el) {
    canvasRefs[page] = el
  }
}

// 选择PDF文件
const choosePdf = () => {
  fileInput.value.click()
}

// 处理文件变化
const handleFileChange = async (event) => {
  const file = event.target.files[0]
  if (!file) return

  // 检查文件大小
  const fileSizeInMB = file.size / 1048576
  if (fileSizeInMB > 10) {
    alert("文件大小不能>10M")
    return
  }

  fileName.value = file.name
  fileSize.value = fileSizeInMB.toFixed(2) + "Mb"
  loading.value = true

  try {
    const arrayBuffer = await file.arrayBuffer()
    pdfDoc = await pdfjsLib.getDocument({ data: arrayBuffer }).promise
    totalPages.value = pdfDoc.numPages
    currentPage.value = 1

    // 清空之前的canvas引用
    Object.keys(canvasRefs).forEach(key => {
      delete canvasRefs[key]
    })

    // 渲染所有页面
    for (let i = 1; i <= totalPages.value; i++) {
      await renderPage(pdfDoc, i)
    }
  } catch (error) {
    console.error('PDF加载失败:', error)
  } finally {
    loading.value = false
  }
}

// 渲染PDF页面
const renderPage = async (pdfFile, pageNumber) => {
  try {
    const page = await pdfFile.getPage(pageNumber)
    const viewport = page.getViewport({ scale })

    // 等待DOM更新
    await new Promise(resolve => setTimeout(resolve, 0))

    const canvas = document.getElementById(`pageNum${pageNumber}`)
    if (canvas) {
      const context = canvas.getContext('2d')
      canvas.width = viewport.width
      canvas.height = viewport.height

      const renderContext = {
        canvasContext: context,
        viewport: viewport
      }

      await page.render(renderContext).promise
      images.value.push(canvas.toDataURL("image/png"));
      let  rawFileAvatar = base64ToFile(canvas.toDataURL("image/png"), "avatar.png")
      images.value.push(URL.createObjectURL(rawFileAvatar));
      console.log(rawFileAvatar,'images.valueimages.value',images.value);

    //   1.将 Canvas 画布内容转换为 PNG 格式的 Base64 数据 URL。eg:canvas.toDataURL("image/png")
    //   2.将base64转换为file  eg: base64ToFile(canvas.toDataURL("image/png")
    //   3.是为 File/Blob 对象创建一个临时的本地 URL 引用 eg:"blob:http://localhost:8087/54e91584-9cd5-4a18-9ecc-c5dec229d0c6
    }
  } catch (error) {
    console.error(`渲染第${pageNumber}页失败:`, error)
  }
}

// 上一页
const prevPage = () => {
  if (currentPage.value <= 1) return
  currentPage.value--
}

// 下一页
const nextPage = () => {
  if (currentPage.value >= totalPages.value) return
  currentPage.value++
}

// 导出图片
const exportImages = async () => {
  if (!pdfDoc) {
    alert('请先上传pdf文件')
    return
  }

  loading.value = true

  try {
    const zip = new window.JSZip()
    const images = zip.folder("images")

    // 遍历canvas,将其生成图片放进文件夹images中
    for (let i = 1; i <= totalPages.value; i++) {
      const canvas = document.getElementById(`pageNum${i}`)
      if (canvas) {
        const imageData = canvas.toDataURL("image/png", 1.0)
        const base64Data = imageData.split(',')[1]
        images.file(`photo-${i}.png`, base64Data, { base64: true })
      }
    }

    // 打包下载
    const content = await zip.generateAsync({ type: "blob" })
    window.saveAs(content, "picture.zip")
  } catch (error) {
    console.error('导出图片失败:', error)
  } finally {
    loading.value = false
  }
}
</script>

<style scoped>
button {
  width: 120px;
  height: 30px;
  background: none;
  border: 1px solid #b1afaf;
  border-radius: 5px;
  font-size: 12px;
  font-weight: 1000;
  color: #384240;
  cursor: pointer;
  outline: none;
  margin: 0 0.5%
}

button:hover {
  background: #ccc;
}

#container {
  width: 600px;
  height: 580px;
  margin-top: 1%;
  border-radius: 2px;
  border: 2px solid #a29b9b;
}

.pdfInfos {
  margin: 0 2%;
}
</style>

如果不想用canvas渲染图片,也可以直接用img渲染图片

javascript 复制代码
<template>
  <div>
    <input type="file" @change="handleFileUpload" accept="application/pdf" />
    <div class="imgItem" v-for="(image, index) in images" :key="index">
      <img :src="image" :alt="'Page ' + (index + 1)" />
    </div>
  </div>
</template>

<script setup>
import { ref } from "vue";
import * as pdfjsLib from "pdfjs-dist/build/pdf";
import "pdfjs-dist/build/pdf.worker.mjs";

const images = ref([]);

const handleFileUpload = async (event) => {
  const file = event.target.files[0];
  if (!file) return;
  const reader = new FileReader();
  reader.onload = async (e) => {
    const pdf = await pdfjsLib.getDocument(e.target.result).promise;
    images.value = [];
    for (let pageNum = 1; pageNum <= pdf.numPages; pageNum++) {
      const page = await pdf.getPage(pageNum);
      const viewport = page.getViewport({ scale: 2 });
      const canvas = document.createElement("canvas");
      const ctx = canvas.getContext("2d");
      canvas.height = viewport.height;
      canvas.width = viewport.width;
      await page.render({ canvasContext: ctx, viewport }).promise;
      images.value.push(canvas.toDataURL("image/png"));
      console.log(images.value,'images.valueimages.value');
    }
  };

  reader.readAsArrayBuffer(file);
};
</script>
<style>
.imgItem {
  margin-bottom: 50px;
}
</style>

还有对图片格式的转化,需要啥图片格式就转换啥

javascript 复制代码
      images.value.push(canvas.toDataURL("image/png"));
      let  rawFileAvatar = base64ToFile(canvas.toDataURL("image/png"), "avatar.png")
      images.value.push(URL.createObjectURL(rawFileAvatar));
      console.log(rawFileAvatar,'images.valueimages.value',images.value);

    //   1.将 Canvas 画布内容转换为 PNG 格式的 Base64 数据 URL。eg:canvas.toDataURL("image/png")
    //   2.将base64转换为file  eg: base64ToFile(canvas.toDataURL("image/png")
    //   3.URL.createObjectURL是为 File/Blob 对象创建一个临时的本地 URL 引用 eg:"blob:http://localhost:8087/54e91584-9cd5-4a18-9ecc-c5dec229d012"格式
相关推荐
时光少年2 小时前
Compose AnnotatedString实现Html样式解析
android·前端
用户904706683572 小时前
假数据生成器——JSONPlaceholder
前端
光影少年2 小时前
react16中的hooks的底层实现原理
前端·react.js·掘金·金石计划
sorryhc2 小时前
手写一个Webpack HMR插件——彻底搞懂热更新原理
前端·javascript·前端工程化
无双_Joney2 小时前
[更新迭代 - 1] Nestjs 在24年底更新了啥?(bug修复篇)
前端·后端·node.js
xiaoxiao无脸男3 小时前
three.js
开发语言·前端·javascript
木易 士心3 小时前
Vue 自定义指令详解
javascript·vue.js·ecmascript
90后的晨仔3 小时前
Vue 组件注册详解:全局注册 vs 局部注册
前端·vue.js
前端Hardy3 小时前
HTML&CSS:高颜值交互式日历,贴心记录每一天
前端·javascript·css