Как я могу использовать 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-файл отображает предварительный просмотр, а остальные - нет.
Проблема в том, что вы вызываете свой скрипт только 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}`);
Вот и все.
Отказ от ответственности: я не проверял это, но это определенно направление.