保留各家工作表报价原文、公式、独立执行日期及签署原件;审核后按执行日期管理版本。33项测试、336项断言通过。生产业务迁移已获明确授权,服务器私有备份已完成并逐条校验。
60 lines
2.8 KiB
PHP
60 lines
2.8 KiB
PHP
<?php
|
||
|
||
namespace App\Support;
|
||
|
||
use Illuminate\Support\Facades\Storage;
|
||
use Illuminate\Validation\ValidationException;
|
||
use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
|
||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||
|
||
final class CourierStockQuoteImageArchive
|
||
{
|
||
/** 原样复制工作表内嵌图片,不使用屏幕截图、裁剪结果或重新编码的图片冒充原件。 */
|
||
public function store(Worksheet $sheet, string $workbookPath, string $directory, array &$savedPaths): array
|
||
{
|
||
$files = $warnings = [];
|
||
$prefix = 'zip://' . realpath($workbookPath) . '#';
|
||
foreach ($sheet->getDrawingCollection() as $index => $drawing) {
|
||
if (! $drawing instanceof Drawing || ! str_starts_with($drawing->getPath(), $prefix)) {
|
||
$warnings[] = '图片 ' . ($index + 1) . ' 无法无损提取,请另附原图;整本 Excel 已保留。';
|
||
continue;
|
||
}
|
||
$stream = @fopen($drawing->getPath(), 'rb');
|
||
if ($stream === false) {
|
||
$this->failed($sheet);
|
||
}
|
||
try {
|
||
$bytes = stream_get_contents($stream, 20 * 1024 * 1024 + 1);
|
||
} finally {
|
||
fclose($stream);
|
||
}
|
||
if ($bytes === false || $bytes === '' || strlen($bytes) > 20 * 1024 * 1024) {
|
||
$this->failed($sheet);
|
||
}
|
||
$mime = (new \finfo(FILEINFO_MIME_TYPE))->buffer($bytes);
|
||
$extension = strtolower(pathinfo($drawing->getPath(), PATHINFO_EXTENSION));
|
||
// 不支持浏览器预览的原始格式仍按原字节留存,只提供下载。
|
||
$extension = preg_match('/^[a-z0-9]{1,8}$/', $extension) ? $extension : 'bin';
|
||
$name = $sheet->getTitle() . '-原件图片-' . ($index + 1) . '.' . $extension;
|
||
$path = $directory . '/' . ($index + 1) . '.' . $extension;
|
||
$savedPaths[] = $path;
|
||
if (! Storage::disk('uploads')->put($path, $bytes, 'private')) {
|
||
$this->failed($sheet);
|
||
}
|
||
$files[] = [
|
||
'kind' => 'signed', 'path' => $path, 'name' => $name, 'sha256' => hash('sha256', $bytes),
|
||
'mime_type' => $mime, 'size' => strlen($bytes), 'sheet_name' => $sheet->getTitle(),
|
||
'anchor' => $drawing->getCoordinates(), 'image_index' => $index + 1,
|
||
'source' => 'workbook_embedded', 'archived_at' => now()->toIso8601String(),
|
||
];
|
||
}
|
||
|
||
return ['signed_images' => $files, 'image_warnings' => array_values(array_unique($warnings))];
|
||
}
|
||
|
||
private function failed(Worksheet $sheet): never
|
||
{
|
||
throw ValidationException::withMessages(['source_quote' => '工作表「' . $sheet->getTitle() . '」原件图片提取或存档失败(每张最多 20 MB),本次上传未完成,请重试。']);
|
||
}
|
||
}
|