简体   繁体   English

如何使用 pdf.js 显示每个用户上传的 pdf 文件的第一页作为 Django 中的预览?

[英]How can I use pdf.js to display the first page of every user-uploaded pdf file as a preview in Django?

Up to now i have successfully display the first page preview for one pdf file but it doesn't work for remaining others.到目前为止,我已经成功地显示了一个 pdf 文件的第一页预览,但它不适用于其他文件。

models.py模型.py

import uuid
from django.db import models


class PdfUploader(models.Model):
    uuid = models.UUIDField(unique=True, default=uuid.uuid4, editable=False)
    docfile = models.FileField(upload_to='documents/%Y/%m/%d')
    uploaded_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        db_table = 'pdf_uploader'
        ordering = ['-uploaded_at']

    @property
    def filename(self):
        return self.docfile.name.split("/")[4].replace('_',' ').replace('-',' ')

views.py视图.py

class PdfUploadView(CreateView):
    def get(self, request, *args, **kwargs):
        context = {'form': PdfUploadForm()}
        return render(request, 'partials/pdf_upload_form.htm', context)

    def post(self, request, *args, **kwargs):
        form = PdfUploadForm(request.POST, request.FILES)
        files = request.FILES.getlist('docfile')
        if form.is_valid():
            for f in files:
                file_instance = PdfUploader(docfile=f)
                file_instance.save()
            return HttpResponseRedirect(reverse_lazy('pdf-list'))
        return render(request, 'partials/pdf_upload_form.htm', {'form': form})

pdf_upload_form.htm pdf_upload_form.htm

{% block "content" %}
<div role="main" class="main">
    <section class="section section-default pt-5 m-0">
        <div class="container">
            <form method="post" enctype="multipart/form-data">
                {% csrf_token %}
                {{ form.as_p }}
                <button type="submit">Upload</button>
            </form>
        </div>
    </section>
</div>
{% endblock %}

pdf_lists.htm By following official official django docs , I am passing a context variable as JSON to the pdf.js. pdf_lists.htm按照官方官方 django 文档,我将上下文变量作为 JSON 传递给 pdf.js。

{% for obj in pdfs %}
    <tr>
         <td>
              {{ forloop.counter }}
         </td>
         <td>
             <a href="{{ obj.docfile.url }}" target="_blank" rel="noopener noreferrer">{{obj.filename}}</a>
         </td>
         <td>
              {{ obj.uploaded_at|date:"d-M-Y" }}
         </td>
         <td>
             <a href="{{obj.docfile.url}}" target="_blank" rel="noopener noreferrer">
                 <canvas id="the-canvas" style="height:250px;">
                 </canvas>
                 {{obj.docfile.url|json_script:'mydata'}}
             </a>
         </td>
    </tr>
{% endfor %}

pdf.js Now I'm reading the previously passed JSON, which contains the path to the user's submitted pdf file to further process it using JS to display the first page of pdf as preview. pdf.js Now I'm reading the previously passed JSON, which contains the path to the user's submitted pdf file to further process it using JS to display the first page of pdf as preview.

const mydata = JSON.parse(document.getElementById('mydata').textContent);
console.log(mydata);

// The workerSrc property shall be specified.
pdfjsLib.GlobalWorkerOptions.workerSrc = '//mozilla.github.io/pdf.js/build/pdf.worker.js';

// Asynchronous download of PDF

var loadingTask = pdfjsLib.getDocument(mydata);
loadingTask.promise.then(function (pdf) {
    console.log('PDF loaded');

    // Fetch the first page
    var pageNumber = 1;
    pdf.getPage(pageNumber).then(function (page) {
        console.log('Page loaded');

        var scale = 0.5;
        var viewport = page.getViewport({ scale: scale });

        // Prepare canvas using PDF page dimensions
        var canvas = document.getElementById('the-canvas');
        var context = canvas.getContext('2d');
        canvas.height = viewport.height;
        canvas.width = viewport.width;

        // Render PDF page into canvas context
        var renderContext = {
            canvasContext: context,
            viewport: viewport
        };
        var renderTask = page.render(renderContext);
        renderTask.promise.then(function () {
            console.log('Page rendered');
        });
    });
}, function (reason) {
    // PDF loading error
    console.error(reason);
});

screenshot of the result: As you can see, the first pdf file displays a preview, while the rest do not.结果截图:如您所见,第一个 pdf 文件显示预览,而 rest 没有。 在此处输入图像描述

The problem is that you only call your script 1 time.问题是你只调用你的脚本 1 次。 Its input is 1 object with id 'mydata' and one canvas with id 'the-canvas' that serves as output.它的输入是 1 个带有 id 'mydata' 的 object 和一个带有 id 'the-canvas' 的 canvas ,用作 output。

What you should do:你应该做什么:

First, assign a unique ID to each data and to each canvas element.首先,为每个数据和每个canvas元素分配一个唯一 ID。

For canvas it is simple:对于 canvas 它很简单:

  <canvas id="the-canvas{{ forloop.counter }}" style="height:250px;">
  </canvas>

For data it is a little more complex due to json_script filter, something like this:对于数据,由于 json_script 过滤器,它有点复杂,如下所示:

{% with mydata_id="mydata"|add:forloop.counter %}
    {{obj.docfile.url|json_script:mydata_id}}
{% endwith %}

Then you also have to know the length of pdfs , so maybe add something like this after the for loop, quick and dirty:然后你还必须知道pdfs的长度,所以也许在 for 循环之后添加这样的东西,快速而肮脏:

<script> const mypdfslength = {{ pdfs | length }}; </script>

Just make sure to place this before your JS.只要确保把它放在你的 JS 之前。

Next, in your js, you have to put all your code inside a for loop from 0 to mypdfslength .接下来,在您的 js 中,您必须将所有代码放入从 0 到mypdfslength的 for 循环中。

And, of course, when resolving mydata and canvas , make sure to reference them by their new ID, which would be, considering i is your for loop index:当然,在解析mydatacanvas时,请确保通过它们的新 ID 引用它们,考虑到i是您的 for 循环索引:

const mydata = JSON.parse(document.getElementById(`mydata{i}`).textContent);

and

var canvas = document.getElementById(`the-canvas{i}`);

That's it.而已。

Disclaimer: I didn't really test it, but that is definitely the direction.免责声明:我没有真正测试它,但这绝对是方向。

It can be achieved by creating specific canvas for each PDF file.可以通过为每个 PDF 文件创建特定的 canvas 来实现。 Please replace the PDF file with your server files.请将 PDF 文件替换为您的服务器文件。

Here is the js code这是js代码

function LoadAndPrint()
{
    var files = [{name:'sample1.pdf',url:'https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/examples/learning/helloworld.pdf'},{name:'sample2.pdf',url:'https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/examples/learning/helloworld.pdf'}];

    var pdfjsLib = window['pdfjs-dist/build/pdf'];
    pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://mozilla.github.io/pdf.js/build/pdf.worker.js';

    files.forEach(myFunc);
    function myFunc(file,i){

        idContainer.innerHTML += 
        '<span>'+file.name+'</span><canvas id="the-canvas'+i+'"></canvas><hr><br>';

        var loadingTask = pdfjsLib.getDocument(file.url);
        loadingTask.promise.then(function(pdf) {
            console.log('PDF loaded');

            var pageNumber = 1;
            pdf.getPage(pageNumber).then(function(page) {
            console.log('Page loaded');

                var scale = 1.1;
                var viewport = page.getViewport({scale: scale});

                var canvas = document.getElementById("the-canvas"+i);
                var context = canvas.getContext('2d');
                canvas.height = viewport.height;
                canvas.width = viewport.width;

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

                var renderTask = page.render(renderContext);
                renderTask.promise.then(function () {
                  console.log('Page rendered');
                });
            });
        }, function (reason) {
          console.error(reason);
        });
    }
}

.html .html

<!DOCTYPE html>
    <head>
        <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
        <script src="https://mozilla.github.io/pdf.js/build/pdf.js"></script>
    </head>

    <body>
        <button id="idPrint" onclick="LoadAndPrint()">Load and Print</button><br>
        <div id="idContainer"></div>
    </body>
</html>

And here is the JSFiffle这是JSFiffle

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM