使用 ghostscript 压缩 PDF 时忽略临时文件

Omit temporary files when compression PDF with ghostscript

为了在将上传的 PDF 文件存储到数据库之前对其进行压缩,我在 mojolicious 控制器中使用了以下代码:

    # if > 100k compress with gs
    my $pdf;
    if ($size > 100_000) {
        # create tmp-file to be read by gs
        my $tmp_fn = '/tmp/badb_pdf_input.pdf';
        $file->move_to("$tmp_fn");

        use Capture::Tiny 'capture';
        my ($stdout, $stderr, $exit) = capture {
            my $cmd = '/usr/local/bin/gs';
            my @args = (qw( -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 -dPDFSETTINGS=/ebook -dNOPAUSE -dQUIET -dBATCH -sOutputFile=- )) ;
            push @args, $tmp_fn;
            system($cmd, @args) == 0
                 or die "system @args failed: $!"
        };
        die "ERROR compressing pdf: $stderr" if $stderr;
        unlink $tmp_fn;
        $pdf = $stdout;
    } else {
        $pdf = $file->slurp;
    }

有谁知道避免输入临时文件 (/tmp/badb_pdf_input.pdf) 的方法吗?

好的,首先你不是 'compressing the PDF file'。您正在做的是解释原始 PDF 文件,创建一系列标记操作,然后根据这些标记操作创建一个新的 PDF 文件。这不是一回事,了解差异很重要。

例如,其中一件可能的事情是对数据进行颜色转换,或降低图像的分辨率(这两种情况都可能在您 select /ebook 时发生)。如果您只是 'compressed' 文件,则不会更改数据,因此不可能进行此类更改。

但是,您也可能会丢失信息。 Ghostscript 的 pdfwrite 设备的唯一目标是视觉外观应该保持不变(在合理的范围内,如果您更改分辨率等等)。元数据可能不会被保留。实际上,pdfwrite 设备不保留某些元数据(例如嵌入式 Illustrator 文件)这一事实是它可以生成较小 PDF 文件的部分原因。

我对 'mojolicious' 一无所知,但您似乎试图通过标准输入将数据发送到 Ghostscript 并从标准输出读取生成的 PDF 文件?

如果是这样,那么您实际上会创建一些临时文件。通常,无法从标准输入处理 PDF 文件,因为 PDF 格式需要随机访问文件。因此,如果您将 PDF 文件通过管道传输到标准输入,Ghostscript 要做的第一件事就是创建一个临时文件并将从标准输入输入的 PDF 文件放入其中。然后它可以解释文件。此外,pdfwrite 将在创建输出时创建大量临时文件。

您 'can' select stdout 作为 PDF 文件的目标,但是.....

正如我提到的,PDF 格式是随机访问的,通常的做法是编写文件的一部分,为您还不知道的位留下 space,然后倒回文件并填充它们在你做的时候。显然,这不适用于不可搜索的流。目前 pdfwrite 设备仅在创建线性化(针对快速 Web 视图进行了优化)PDF 文件时执行此操作,但我不保证未来版本的 pdfwrite 不需要在输出文件中查找的能力。

所以简短的回答是您可以将 OutputFile 设置为 stdout,但不能保证一定有效。

如果上传的 pdf 文件大小小于 256 KB(默认情况下,请参阅 max_memory_size),那么您不需要将其保存为临时文件,因为它已经保存了。

Here 是如何获取文件路径的最小示例。