簡體   English   中英

在 Django 中如何將上傳的 pdf 文件轉換為圖像文件並保存到數據庫中的相應列?

[英]In Django how to convert an uploaded pdf file to an image file and save to the corresponding column in database?

我正在創建一個 HTML 模板來顯示 pdf 文件的封面(第一頁或用戶可以選擇一個)。 我希望 Django 無需額外上傳即可自動創建封面圖像。

pdf 文件是使用 Django Modelform 上傳的。 這是我的代碼的結構

模型.py

class Pdffile(models.Model):
    pdf = models.FileField(upload_to='pdfdirectory/')
    filename = models.CharField(max_length=20)
    pagenumforcover = models.IntegerField()
    coverpage = models.FileField(upload_to='coverdirectory/')

表格.py

class PdffileForm(ModelForm):
    class Meta:
        model = Pdffile
        fields = (
            'pdf',
            'filename',
            'pagenumforcover',
        )

視圖.py

def upload(request):
    if request.method == 'POST':
        form = PdffileForm(request.POST, request.FILES)
        if form.is_valid():
            form.save()
            return redirect('pdffilelist')
    else:
        form = PdffileForm()
    return render(request, "uploadform.html", {'form': form})


def pdfcover(request, pk):
    thispdf = get_object_or_404(Pdffile, pk=pk)

    return render(request, 'pdfcover.html', {'thispdf': thispdf})

在“pdfcover.html”中,我想使用 Django 模板語言,這樣我就可以為不同的上傳 pdf 文件呈現不同的 HTML。 這就是為什么我要將圖像文件保存到與 pdf 文件相同的列中。

我是 Python 的新手,Django 的新手,顯然是堆棧溢出的新手。 我已經嘗試過 pdf2image 和 PyPDF2,我相信它們都可以工作,但是我找不到正確的代碼。 如果各位大神賜教,我將不勝感激。

pdf2image package 中有一個名為convert_from_path的 function 。

這是 package 內部對 function 的每個參數的作用的描述。

Parameters:
            pdf_path -> Path to the PDF that you want to convert
            dpi -> Image quality in DPI (default 200)
            output_folder -> Write the resulting images to a folder (instead of directly in memory)
            first_page -> First page to process
            last_page -> Last page to process before stopping
            fmt -> Output image format
            jpegopt -> jpeg options `quality`, `progressive`, and `optimize` (only for jpeg format)
            thread_count -> How many threads we are allowed to spawn for processing
            userpw -> PDF's password
            use_cropbox -> Use cropbox instead of mediabox
            strict -> When a Syntax Error is thrown, it will be raised as an Exception
            transparent -> Output with a transparent background instead of a white one.
            single_file -> Uses the -singlefile option from pdftoppm/pdftocairo
            output_file -> What is the output filename or generator
            poppler_path -> Path to look for poppler binaries
            grayscale -> Output grayscale image(s)
            size -> Size of the resulting image(s), uses the Pillow (width, height) standard
            paths_only -> Don't load image(s), return paths instead (requires output_folder)
            use_pdftocairo -> Use pdftocairo instead of pdftoppm, may help performance
            timeout -> Raise PDFPopplerTimeoutError after the given time

因為convert_from_path旨在能夠將 pdf 中的每一頁轉換為圖像,所以 function 返回一個 Image 對象數組。

如果您設置output_folder參數,每個圖像都將從基本目錄保存到該位置。 在這種情況下, output_folder必須是完整路徑,例如'path/from/root/to/output_folder' 如果不設置,轉換時圖像不會保存,僅在 memory 中。

默認情況下,如果您不設置output_file參數,function 將生成一個隨機格式的文件名,例如0a15a918-59ba-4f15-90f0-2ed5fbd0c36c-1.ext 雖然如果你設置了一個文件名,因為這個文件名用於轉換多個 pdf 頁面,如果你的output_file'file_name'那么每個文件將從'file_name0001-1.ext'開始命名。

請注意,如果您設置output_fileoutput_folder並嘗試轉換兩個不同的 pdf,則第二個 pdf 將覆蓋第一個的圖像文件(如果它們位於同一目錄中)。



這是問題中圍繞您的代碼建模的一些代碼。 此代碼假定您已安裝pdf2image

我在pdf上添加了一個內置驗證器,因為如果上傳了 pdf 之外的任何其他內容,代碼將會崩潰。

validators=[FileExtensionValidator(allowed_extensions=['pdf'])]

我還為上傳目錄和文件格式創建了三個常量。 如果您需要更改其中任何一個,則代碼的 rest 可以保持不變。

COVER_PAGE_DIRECTORY = 'coverdirectory/'
PDF_DIRECTORY = 'pdfdirectory/'
COVER_PAGE_FORMAT = 'jpg'

另外我假設你有保存文件的默認設置。

settings.py

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

models.py

from django.core.validators import FileExtensionValidator
from django.db.models.signals import post_save
from pdf2image import convert_from_path
from django.conf import settings
import os


COVER_PAGE_DIRECTORY = 'coverdirectory/'
PDF_DIRECTORY = 'pdfdirectory/'
COVER_PAGE_FORMAT = 'jpg'

# this function is used to rename the pdf to the name specified by filename field
def set_pdf_file_name(instance, filename):
    return os.path.join(PDF_DIRECTORY, '{}.pdf'.format(instance.filename))

# not used in this example
def set_cover_file_name(instance, filename):
    return os.path.join(COVER_PAGE_DIRECTORY, '{}.{}'.format(instance.filename, COVER_PAGE_FORMAT))

class Pdffile(models.Model):
    # validator checks file is pdf when form submitted
    pdf = models.FileField(
        upload_to=set_pdf_file_name, 
        validators=[FileExtensionValidator(allowed_extensions=['pdf'])]
        )
    filename = models.CharField(max_length=20)
    pagenumforcover = models.IntegerField()
    coverpage = models.FileField(upload_to=set_cover_file_name)

def convert_pdf_to_image(sender, instance, created, **kwargs):
    if created:
        # check if COVER_PAGE_DIRECTORY exists, create it if it doesn't
        # have to do this because of setting coverpage attribute of instance programmatically
        cover_page_dir = os.path.join(settings.MEDIA_ROOT, COVER_PAGE_DIRECTORY)

        if not os.path.exists(cover_page_dir):
            os.mkdir(cover_page_dir)

        # convert page cover (in this case) to jpg and save
        cover_page_image = convert_from_path(
            pdf_path=instance.pdf.path,
            dpi=200, 
            first_page=instance.pagenumforcover, 
            last_page=instance.pagenumforcover, 
            fmt=COVER_PAGE_FORMAT, 
            output_folder=cover_page_dir,
            )[0]

        # get name of pdf_file 
        pdf_filename, extension = os.path.splitext(os.path.basename(instance.pdf.name))
        new_cover_page_path = '{}.{}'.format(os.path.join(cover_page_dir, pdf_filename), COVER_PAGE_FORMAT)
        # rename the file that was saved to be the same as the pdf file
        os.rename(cover_page_image.filename, new_cover_page_path)
        # get the relative path to the cover page to store in model
        new_cover_page_path_relative = '{}.{}'.format(os.path.join(COVER_PAGE_DIRECTORY, pdf_filename), COVER_PAGE_FORMAT)
        instance.coverpage = new_cover_page_path_relative

        # call save on the model instance to update database record
        instance.save()

post_save.connect(convert_pdf_to_image, sender=Pdffile)

convert_pdf_to_image是一個 function,它在Pdffile model 的post_save信號上運行。 它會在您的PdffileForm保存在您的上傳視圖中后運行,以便我們可以從保存的 pdf 文件創建封面圖像文件。

cover_page_image = convert_from_path(
            pdf_path=instance.pdf.path,
            dpi=200, 
            first_page=instance.pagenumforcover, 
            last_page=instance.pagenumforcover, 
            fmt=COVER_PAGE_FORMAT, 
            output_folder=cover_page_dir,
            )[0]

改變dpi會改變圖像的質量。 為了只轉換一個頁面, first_pagelast_page參數是相同的。 因為結果是一個數組,所以在這種情況下,我們在cover_page_image內的列表中獲取第一個也是唯一的元素。

對上傳視圖的微小更改。

views.py

def upload(request):

    form = PdffileForm()

    if request.method == 'POST':
        form = PdffileForm(request.POST, request.FILES)
        # if form is not valid then form data will be sent back to view to show error message
        if form.is_valid():
            form.save()
            return redirect('pdffilelist')

    return render(request, "uploadform.html", {'form': form})

我不知道您的upload.html文件是什么樣的,但我使用了以下內容,它將與提供的代碼一起使用。

upload.html

<h1>Upload PDF</h1>

<form method="POST" enctype="multipart/form-data">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">Upload</button>
</form>

以 pdf 為例

示例 pdf

通過表格上傳

上傳表格

生成的數據庫記錄

數據庫記錄

上傳后的文件位置

帶有圖像的文件目錄



最后說明:

因為 FileFields 具有確保現有文件不會被覆蓋的代碼,所以代碼

# get name of pdf_file 
pdf_filename, extension = os.path.splitext(os.path.basename(instance.pdf.name))
new_cover_page_path = '{}.{}'.format(os.path.join(cover_page_dir, pdf_filename), COVER_PAGE_FORMAT)
# rename file to be the same as the pdf file
os.rename(cover_page_image.filename, new_cover_page_path)
# get the relative path to the cover page to store in model
new_cover_page_path_relative = '{}.{}'.format(os.path.join(COVER_PAGE_DIRECTORY, pdf_filename), COVER_PAGE_FORMAT)
instance.coverpage = new_cover_page_path_relative

確保使用 pdf FileField 文件名來命名封面,因為它幾乎是完全唯一的。

重復的文件名

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM