JS逐页转pdf文件为图片格式

背景

年前的时候,开发一个电子杂志项目,功能需求是通过上传pdf文件,将其转为图片格式,所以杂志的内容其实就是一张张图片

不过当时技术要求用后端实现,所以使用的是PHP实现该功能。项目完成后,寻思着在前端是否也能实现pdf转图片的功能。一番研究后,果真可行。以下就分享如何通过前端js将pdf文件转为图片格式,并且支持翻页预览、以及图片打包下载

效果预览

所需工具

  1. pdf.js(负责API解析,可将pdf文件渲染成canvas实现预览)
  2. pdf.worker.js(负责核心解析)
  3. jszip.js(将图片打包成生成.zip文件)
  4. Filesaver.js(保存下载zip文件)

工具下载

一、pdf.js及pdf.worker.js下载地址:
http://mozilla.github.io/pdf.js/getting_started/#download

1.选择稳定版下载

2.解压后将bulid中的pdf.js及pdf.worker.js拷贝到项目中

二、jszip.js及Filesaver.js下载地址:
https://stuk.github.io/jszip/

1.点击download.JSZip

2.解压后将dist文件夹下的jszip.js文件以及vendor文件夹下的FileSaver.js文件拷贝到项目中


至此,所需工具已齐全。以下直接附上项目完整代码(代码可直接复制使用,查看效果。对应的文件需自行下载引入)

源代码:嫌麻烦的小伙伴可以直接在公众号后回复:pdf转图片

代码实现

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>PDF文件转图片</title>
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
<script type="text/javascript" src="js/pdf.js"></script>
<script type="text/javascript" src="js/pdf.worker.js"></script>
<script type="text/javascript" src="js/jszip.js"></script>
<script type="text/javascript" src="js/FileSaver.js"></script>
<style type="text/css">

  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: 780px;
      margin-top: 1%;
    border-radius: 2px;
    border: 2px solid #a29b9b;
  }

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

<body>

  <div style="margin-top:1%">            
      <button id="prevpage">上一页</button>
      <button id="nextpage">下一页</button>
    <button id="exportImg">导出图片</button>
    <button onclick="choosePdf()">选择一个pdf文件</button>
    <input style="display:none" id='chooseFile' type='file' accept="application/pdf">
  </div>

  <div style="margin-top:1%">
    <span class="pdfInfos">页码:<span id="currentPages"></span><span id="totalPages"></span></span>
    <span class="pdfInfos">文件名:<span id="fileName"></span></span>
    <span class="pdfInfos">文件大?。?lt;span id="fileSize"></span></span>
  </div>

  <div style="position: relative;">
    <div id="container"></div>
    <img id="imgloading" style="position: absolute;top: 20%;left: 2%;display:none" src="loading.gif">
  </div>

</body>


<script>

  var currentPages,totalPages //声明一个当前页码及总页数变量
  var scale = 2; //设置缩放比例,越大生成图片越清晰

  $('#chooseFile').change(function() {
    var pdfFilePath = $('#chooseFile').val();
    if(pdfFilePath) {

      $("#imgloading").css('display','block');
      $("#container").empty(); //清空上一PDF文件展示图

      currentPages=1; //重置当前页数
      totalPages=0; //重置总页数

        var filesdata = $('#chooseFile')[0].files; //jquery获取到文件 返回属性的值
      var fileSize = filesdata[0].size; //文件大小
      var mb;

      if(fileSize) {
        mb = fileSize / 1048576;
        if(mb > 10) {
          alert("文件大小不能>10M");
          return;
        }
      }

      $("#fileName").text(filesdata[0].name);
      $("#fileSize").text(mb.toFixed(2) + "Mb");

      var reader = new FileReader();
      reader.readAsDataURL(filesdata[0]); //将文件读取为 DataURL
      reader.onload = function(e) { //文件读取成功完成时触发

        pdfjsLib.getDocument(this.result).then(function(pdf) { //调用pdf.js获取文件
          if(pdf) {
            totalPages = pdf.numPages; //获取pdf文件总页数
            $("#currentPages").text("1/");
            $("#totalPages").text(totalPages);

            //遍历动态创建canvas
            for(var i = 1; i <= totalPages; i++) {
              var canvas = document.createElement('canvas');
              canvas.id = "pageNum" + i;
              $("#container").append(canvas);
              var context = canvas.getContext('2d');
              renderImg(pdf,i,context);
            }

          }
        });

      };
    }
  });

  //渲染生成图片
  function renderImg(pdfFile,pageNumber,canvasContext) {
    pdfFile.getPage(pageNumber).then(function(page) { //逐页解析PDF
      var viewport = page.getViewport(scale); // 页面缩放比例
      var newcanvas = canvasContext.canvas;

      //设置canvas真实宽高
      newcanvas.width = viewport.width;
      newcanvas.height = viewport.height;

      //设置canvas在浏览中宽高
      newcanvas.style.width = "100%";
      newcanvas.style.height = "100%";

      //默认显示第一页,其他页隐藏
      if (pageNumber!=1) {
         newcanvas.style.display = "none";
      }

      var renderContext = {
        canvasContext: canvasContext,
        viewport: viewport
      };

      page.render(renderContext); //渲染生成
    });

    $("#imgloading").css('display','none');

    return;
  };

  //上一页
  $("#prevpage").click(function(){

        if (!currentPages||currentPages <= 1) {
            return;
        }

    nowpage=currentPages;
        currentPages--;

    $("#currentPages").text(currentPages+"/");

    var prevcanvas = document.getElementById("pageNum"+currentPages);
    var currentcanvas = document.getElementById("pageNum"+nowpage);
    currentcanvas.style.display = "none";
    prevcanvas.style.display = "block";

  })

  //下一页
  $("#nextpage").click(function(){

    if (!currentPages||currentPages>=totalPages) {
      return;
    }

    nowpage=currentPages;
    currentPages++;

    $("#currentPages").text(currentPages+"/");

    var nextcanvas = document.getElementById("pageNum"+currentPages);
    var currentcanvas = document.getElementById("pageNum"+nowpage);
    currentcanvas.style.display = "none";
    nextcanvas.style.display = "block";

  })

  //导出图片
  $("#exportImg").click(function() {

    if (!$('#chooseFile').val()) {
      alert('请先上传pdf文件')
      return false;
    }

    $("#imgloading").css('display','block');

    var zip = new JSZip(); //创建一个JSZip实例
    var images = zip.folder("images"); //创建一个文件夹用来存放图片

    //遍历canvas,将其生成图片放进文件夹images中
    $("canvas").each(function(index, ele) {
      var canvas = document.getElementById("pageNum" + (index + 1));

      //将图片放进文件夹images中
      //参数1为图片名称,参数2为图片数据(格式为base64,需去除base64前缀 data:image/png;base64)
      images.file("photo-" + (index + 1) + ".png", splitBase64(canvas.toDataURL("image/png", 1.0)), {
        base64: true
      });

    })

    //打包下载
    zip.generateAsync({
      type: "blob"
    }).then(function(content) {
      saveAs(content, "picture.zip"); //saveAs依赖的js文件是FileSaver.js
        $("#imgloading").css('display','none');
    });

  });

  //截取base64前缀
  function splitBase64(dataurl) {
    var arr = dataurl.split(',')
    return arr[1]
  }

  function choosePdf(){
    $("#chooseFile").click()
  }
</script>
</html>

项目实现原理分析

  1. 首先利用pdf.js将上传的pdf文件转化成canvas
  2. 然后使用jszip.js将canvas打包图片生成.zip文件
  3. 最后使用Filesaver.js将zip文件保存下载

项目注意要点

  1. 由于pdf文件是通过上传的,因此需要通过js的FileReader()对象将其读取为DataURL,pdf.js文件才可读取渲染
  2. JSZip对象的.file()函数中第二个参数传入的是base64格式图片,但是要去掉base64前缀标识

最后

觉得文章不错的,请点个赞哇!
文章首发于微信公众号:GitWeb,欢迎关注学习技术讨论交流。
微信交流群:公众号内加好友,拉你入群

欢迎关注
?著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,992评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,212评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事?!?“怎么了?”我有些...
    开封第一讲书人阅读 159,535评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,197评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,310评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,383评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,409评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,191评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,621评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,910评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,084评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,763评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,403评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,083评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,318评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,946评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,967评论 2 351

推荐阅读更多精彩内容