You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel导入带图片Excel报file_put_contents目录不存在错误如何修复

报错核心原因

你遇到的failed to open stream: No such file or directory报错,本质是两个核心问题:

  1. 写入文件时使用了相对路径images/detail_pengajuan/,PHP执行时的工作目录和你预期的项目根目录不一致,解析出的路径根本不存在
  2. 没有提前创建目标存储目录,也没有做目录存在性校验,程序不会自动生成缺失的目录层级
    另外你贴的代码里还有3个影响功能的逻辑bug,就算手动建好目录,也会出现图片和行不匹配、文件名重复覆盖、大文件导入超时/内存溢出的问题。
修复步骤
  • 修正路径写法:所有文件读写都用绝对路径。Laravel框架中公共可访问的文件统一放在public目录下,写入时用public_path()辅助函数拼接完整路径,避免工作目录变化导致的路径解析错误。
  • 增加目录自动创建逻辑:不要依赖手动建目录,可以用Laravel自带的File::ensureDirectoryExists()方法,在写入文件前自动检测目录,不存在就按指定权限递归创建。
  • 修正原有逻辑缺陷:
    • 把加载Excel、提取图片的逻辑从onRow方法移到构造函数中,不要每处理一行就重新加载一次整个文件,否则大文件会直接内存溢出、请求超时。
    • 提取图片时记录图片所在的行坐标,处理每行数据时只取当前行对应的图片,避免所有行都匹配到最后一张图片的问题。
    • 文件名生成不要只用time() + 自增计数器,同一秒内处理多行很容易出现重名覆盖,改用uniqid()生成唯一文件名。
    • 计数器$i不要放在行处理逻辑内,否则每处理一行都会重置,完全起不到去重作用。
修正后的代码参考

首先在类头部引入文件操作类:

use Illuminate\Support\Facades\File;

完整实现代码:

namespace App\Imports;

use App\SubmissionDetail;
use Maatwebsite\Excel\Row;
use Maatwebsite\Excel\Concerns\OnEachRow;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing;
use Illuminate\Support\Facades\File;

class SubmissionDetailImport implements OnEachRow, WithHeadingRow
{
    protected $id;
    // 提前存储所有行对应的图片数据
    protected $rowImages = [];

    function __construct($id) {
        $this->id = $id;
        // 初始化目标存储目录,不存在自动递归创建,权限设为755
        $targetDir = public_path('images/detail_pengajuan');
        File::ensureDirectoryExists($targetDir, 0755, true);

        // 只加载一次Excel文件,提前提取所有图片和对应行号
        $spreadsheet = IOFactory::load(request()->file('file'));
        $worksheet = $spreadsheet->getActiveSheet();
        foreach ($worksheet->getDrawingCollection() as $drawing) {
            // 解析图片所在的行坐标
            preg_match('/[A-Z]+([0-9]+)/', $drawing->getCoordinates(), $match);
            $currentRow = (int)$match[1];

            // 提取图片内容和扩展名
            if ($drawing instanceof MemoryDrawing) {
                ob_start();
                call_user_func(
                    $drawing->getRenderingFunction(),
                    $drawing->getImageResource()
                );
                $imageContents = ob_get_contents();
                ob_end_clean();
                switch ($drawing->getMimeType()) {
                    case MemoryDrawing::MIMETYPE_PNG :
                        $extension = 'png';
                        break;
                    case MemoryDrawing::MIMETYPE_GIF:
                        $extension = 'gif';
                        break;
                    case MemoryDrawing::MIMETYPE_JPEG :
                        $extension = 'jpg';
                        break;
                }
            } else {
                $zipReader = fopen($drawing->getPath(), 'r');
                $imageContents = '';
                while (!feof($zipReader)) {
                    $imageContents .= fread($zipReader, 1024);
                }
                fclose($zipReader);
                $extension = $drawing->getExtension();
            }

            $this->rowImages[$currentRow] = [
                'content' => $imageContents,
                'ext' => $extension
            ];
        }
    }

    public function onRow(Row $row)
    {
        $rowData = $row->toArray();
        $currentRowIndex = $row->getIndex();
        $myFileName = null;

        // 只处理当前行绑定的图片
        if (isset($this->rowImages[$currentRowIndex])) {
            $img = $this->rowImages[$currentRowIndex];
            // 生成唯一文件名,避免重名覆盖
            $myFileName = uniqid() . '.' . $img['ext'];
            // 用绝对路径写入文件
            file_put_contents(
                public_path('images/detail_pengajuan/' . $myFileName),
                $img['content']
            );
        }

        $submissionDetails = SubmissionDetail::firstOrCreate(
            ['submission_id' => $this->id, 'nama_barang' => $rowData['nama_barang']],
            [
                'submission_id' => $this->id,
                'nama_barang' => $rowData['nama_barang'],
                'image_path' => $myFileName,
                'jumlah' => $rowData['jumlah'],
                'harga_satuan' => $rowData['harga_satuan'],
                'harga_total' => $rowData['harga_total'],
                'keterangan' => $rowData['keterangan'],
            ]
        );

        if (! $submissionDetails->wasRecentlyCreated) {
            $submissionDetails->update([
                'image_path' => $myFileName,
                'jumlah' => $rowData['jumlah'],
                'harga_satuan' => $rowData['harga_satuan'],
                'harga_total' => $rowData['harga_total'],
                'keterangan' => $rowData['keterangan'],
            ]);
        }
    }
}
额外注意事项
  • 如果是Linux服务器部署,要确保public/images目录的所有者和web运行用户(一般是www-data、nginx用户)一致,避免权限不足无法写入。
  • 更规范的做法是把用户上传的文件存在storage/app/public目录下,执行php artisan storage:link创建软链对外访问,框架会自动处理路径和权限问题,比手动操作public目录更安全。

内容的提问来源于stack exchange,提问作者Ipenk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 05:21:23