Как я могу использовать pdf.js для отображения первой страницы каждого загруженного пользователем pdf-файла в качестве предварительного просмотра в Django?

До сих пор я успешно отображал предварительный просмотр первой страницы для одного pdf файла, но он не работает для остальных.

models.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

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

{% 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 Следуя официальным официальным документам 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. Теперь я читаю ранее переданный JSON, который содержит путь к отправленному пользователем pdf-файлу, чтобы затем обработать его с помощью JS для отображения первой страницы pdf в качестве предварительного просмотра.

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);
});

Скриншот результата: Как видите, первый pdf-файл отображает предварительный просмотр, а остальные - нет. enter image description here

Проблема в том, что вы вызываете свой скрипт только 1 раз. Его входными данными является 1 объект с id 'mydata' и один холст с id 'the-canvas', который служит выходом.

Что вам следует делать:

Сначала присвойте уникальный ID каждому элементу data и каждому элементу canvas.

Для холста это просто:

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

Для данных это немного сложнее из-за фильтра json_script, что-то вроде этого:

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

Тогда вам также нужно знать длину pdfs, поэтому, возможно, добавьте что-то вроде этого после цикла for, быстро и грязно:

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

Просто убедитесь, что вы поместили это перед вашим JS.

Далее, в вашем js, вы должны поместить весь ваш код внутри цикла for от 0 до mypdfslength.

И, конечно, при разрешении mydata и canvas обязательно ссылайтесь на них по их новому ID, который будет, учитывая, что i - это индекс цикла for:

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

и

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

Вот и все.

Отказ от ответственности: я не проверял это, но это определенно направление.

Вернуться на верх