如何使用 pdf.js 将每个用户上传的 pdf 文件的第一页显示为 Django 中的预览?
Posted
技术标签:
【中文标题】如何使用 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? 【发布时间】:2021-12-29 02:16:17 【问题描述】:到目前为止,我已成功显示一个 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 通过关注官方official django docs,我将上下文变量作为 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 次。它的输入是 1 个 ID 为“mydata”的对象和一个 ID 为“the-canvas”的画布作为输出。
你应该做什么:
首先,为每个 data 和每个 canvas 元素分配一个唯一 ID。
画布很简单:
<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 中,您必须将所有代码放入从 0 到 mypdfslength
的 for 循环中。
当然,在解析 mydata
和 canvas
时,请确保通过它们的新 ID 引用它们,考虑到 i
是你的 for 循环索引:
const mydata = JSON.parse(document.getElementById(`mydatai`).textContent);
和
var canvas = document.getElementById(`the-canvasi`);
就是这样。
免责声明:我没有真正测试它,但这绝对是方向。
【讨论】:
非常感谢您调查此问题。我按照上面概述的说明进行操作。但是现在它抛出了这个错误 Uncaught TypeError: Cannot read properties of null (reading 'textContent') 正如我所提到的,这是解决问题的方向,而不是秘诀。所以它找不到元素mydata0
或任何数字。我建议您通过查看您获得的页面的 html 源代码来调试它。您在数据脚本子句中看到了哪些 ID?它们是否正确生成,它们的格式是mydataNUMBER
吗?
我检查了我的 html 源代码,一切都按正确的顺序排列。并且上述错误已经以某种方式得到解决。错误消息现在是“在多个渲染操作期间不能使用同一个画布”。 p.s.我对javascript不熟悉。【参考方案2】:
可以通过为每个 PDF 文件创建特定的画布来实现。 请用您的服务器文件替换 PDF 文件。
这是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
<!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>
这里是 JSFiffle
【讨论】:
以上是关于如何使用 pdf.js 将每个用户上传的 pdf 文件的第一页显示为 Django 中的预览?的主要内容,如果未能解决你的问题,请参考以下文章