【问题标题】:Laravel multiple files (save all or fail all )Laravel 多个文件(全部保存或全部失败)
【发布时间】:2021-09-13 21:47:34
【问题描述】:

我正在制作一个使用多个图像创建服务的表单,我不确定我是否正在做正确的处理失败的保存过程。

例如,如果我有 5 张图片并且已经上传了 3 张但在第 4 张图片中失败了,那么我需要取消保存过程并删除所有保存的文件。

$validatedImages = [];

    foreach($images as $key => $image){

        $imageName = ServiceImage::generateRecordName($image);

        if(!$image->storeAs(ServiceImage::path(), $imageName)) {

            // fall back all the stored files
            foreach($validatedImages as $validatedImage)
                Storage::delete(ServiceImage::path() . $validatedImage);

            return redirect()->back()->with(['errorMsg' => 'There was a problem when uploading images']);

        }

        $validatedImages[] = $imageName;

    }

以及存储到数据库时

foreach($validatedImages as $validatedImage)
        if(!$service->images()->save(new ServiceImage(['name' => $validatedImage])))
            // handle failure .. 

所以我的问题是:处理这种回退的最佳做法是什么。

【问题讨论】:

  • MySQL 具有“事务”,可以专门执行“全部或无”。 Laravel 有办法解决这个问题吗?请参阅“开始”和“提交”。

标签: php mysql laravel validation


【解决方案1】:

您的控制器应该只接收完全验证的数据。因此,一旦您进入控制器方法的主体,这些图像就应该可以上传了:

$paths = [];

foreach ($request->file('images') as $image) {
    $paths[] = $image->store('images');
}

// Do something with $paths

您可能希望异步上传图片。特别是如果请求中有五个。使用这种方法,您可以将文件上传到定期清理的文件夹(比如 24 小时后)。将图像上传到此文件夹时,返回路径。提交控制器操作的路径而不是实际文件,然后您可以将文件从临时文件夹移动到永久文件夹。

【讨论】:

  • 表单数据在FormRequest 中验证,但我不知道store()storeAs() 是否会失败,以及在这种情况下会发生什么,或者保存查询是否失败。那么有没有办法让他们喜欢(全部或不)?或者我夸大了。
【解决方案2】:

您的方法可以改进,因为我从您的代码中看到的是,一旦您上传文件,您所做的就是删除文件,然后在另一个循环中再次检查数据库上传。因为当所有文件都正确上传但无法写入数据库时​​,您将再次删除所有这些文件。这对于小文件可能无关紧要,但是当文件很大并且文件数量也很大时,这可能会导致不必要的性能问题。例如,您上传了 100 个文件,每个文件大小为 10 mb,然后您上传了 1000mb,但是您在数据库中执行的文件在第一次数据库更新时显示错误,然后上传剩余的 99 个文件是徒劳的。此外,您正在制作四个循环,2 个用于上传,2 个用于数据库,我在这里看到了一些不必要的额外循环。使用此代码可以将循环从 4 个减少到 2 个,并且您可以在文件出现错误时停止脚本,而无需转到另一个文件,从而节省流量并提高性能。

关键是您上传每个文件并将其写入数据库并在上传或数据库代码执行成功出现错误时立即停止脚本, 这就是我建议您改进代码的方法。

$validatedImages = [];
    
        foreach($images as $key => $image){
    
            $imageName = ServiceImage::generateRecordName($image);
    
            if(!$image->storeAs(ServiceImage::path(), $imageName)) {
                    $this->deleteImage($validatedImages);
                return redirect()->back()->with(['errorMsg' => 'There was a problem when uploading images']);
    
            }
            if(!$service->images()->save(new ServiceImage(['name' => $imageName]))){
            /*file has been uploaded but database error was found so this file needs to be deleted, */
            $this->deleteImage($validatedImages,$imageName);
        
            return redirect()->back()->with(['errorMsg' => 'There was a problem updating the database']);
        }   
    
            $validatedImages[] = $imageName;    
        }
    
    
    function deleteImage($validatedImages,$extraimage=null){    
        foreach($validatedImages as $validatedImage){
            Storage::delete(ServiceImage::path() . $validatedImage);
            //code for reversing database change        
        }                    
        if($extraimage)
        /*the image which was uploaded but could not be written in the 
             database*/
        Storage::delete(ServiceImage::path() . $extraimage);
    
    }

您可以做的另一种方法是将图像存储在临时文件夹中,并仅在所有文件都上传后永久移动文件,一旦发生任何错误,您可以删除该临时目录中的所有文件,而不是循环删除它们。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-09-15
    • 1970-01-01
    • 2016-10-13
    • 2021-01-03
    • 1970-01-01
    • 1970-01-01
    • 2017-03-28
    相关资源
    最近更新 更多