问题
What needs to be done:
User has to be able to upload a PDF, then the file is uploaded to an Amazon S3 bucket, the file should be compressed then.
Current environment:
- Laravel application (mounted on Docker) (
php:7.4-fpm-alpine3.11
,GPL Ghostscript 9.50
,Laravel Framework 5.8.37
) - Amazon S3 bucket to save documents in
- Script is in a shell file which is made executable and added to /usr/local/bin as
shrink
- Shell is not explicitly added in Docker container, should it be?
Current flow:
- User uploads file
- File is uploaded to S3
- Laravel then downloads the file to local temp folder
- Ghostscript is then ran to compress said file (this script)
- Compressed file is uploaded back to S3
Problem:
The file is found and being compressed, but the output is a blank (1 page, which is white) pdf file.
Dockerfile:
# Add compression shell script to global executables
COPY .docker/config/shrink.sh /usr/local/bin/shrink
RUN chmod u+x /usr/local/bin/shrink
RUN chown nobody.nobody /usr/local/bin/shrink
nobody
is the user PHP is running on.
PHP function that should compress the file:
public function optimizeFile($file_path)
{
// Copy file from default disk to temp disk
Storage::disk('temp')->put($file_path, Storage::get($file_path));
$fullTempFilePath = Storage::disk('temp')->path($file_path);
if (Storage::mimeType($file_path) == 'application/pdf') {
$output = shell_exec("shrink " . $fullTempFilePath . " " . $fullTempFilePath);
if ($output != null) {
Log::error($output);
}
} else {
ImageOptimizer::optimize($fullTempFilePath);
}
// Write the compressed file back to default disk
Storage::put($file_path, Storage::disk('temp')->get($file_path));
// Delete temp file
Storage::disk('temp')->delete($file_path);
}
If the file isn't a PDF, ImageOptimizer
does it's job and compresses the image successfully.
What have I tried:
- (Local) Successfully compressed the file without Docker, using
php artisan serve
to launch Laravel app; - (Local) Successfully compressed the file with Docker using
docker exec -it <container_id> shrink in.pdf out.pdf
; - (Local) Successfully compressed the file with Docker in it's shell using
docker exec -it <container_id> /bin/bash
; - (Local) My coworker did the same things locally and the response is also a blank pdf
回答1:
Ok, so the problem is in here:
$output = shell_exec("shrink " . $fullTempFilePath . " " . $fullTempFilePath);
Ghostscript PDF compression does not work as expected if the input and output files are the same. Solution:
$output = shell_exec("shrink " . $fullTempFilePath . $fullTempFilePath . "-compressed ");
shell_exec("mv " . $fullTempFilePath . "-compressed " . $fullTempFilePath);
回答2:
OK so first point; Ghostscript (more accurately the Ghostscript pdfwrite device) does not shrink PDF files. The actual process is described in the documentation in the overview in VectorDevices.htm. I'd reccomend you read it.
Secondly you can't use the same name for the input and output files. Ghostscript will still be reading from the input file while the pdfwrite device wants to write to the output file.
来源:https://stackoverflow.com/questions/60987289/ghostscript-pdf-file-compression-using-phps-exec-laravel-on-docker