Laravel несколько файлов (сохранить все или не все)

avatar
Mustafa Reda
1 июля 2021 в 20:42
159
2
0

Я создаю форму, которая создает службу с несколькими изображениями, я не уверен, правильно ли я делаю процесс сохранения, который обрабатывает сбои.

например, если у меня есть 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 .. 

поэтому мой вопрос: как лучше всего справиться с этим откатом.

Источник
Rick James
12 октября 2021 в 19:24
0

В MySQL есть «транзакции», которые могут выполнять «все или ничего». Есть ли у Laravel способ привязаться к этому? См. «НАЧАТЬ» и «ЗАВЕРШИТЬ».

Ответы (2)

avatar
Prakhar Gyawali
2 июля 2021 в 01:32
0

Ваш метод можно улучшить, поскольку из вашего кода я вижу, что после загрузки файлов все, что вы делаете, это удаляете файл и снова в другом цикле проверяете загрузку базы данных. Поскольку, когда все файлы правильно загружены, но не могут быть записаны в базу данных, вы снова удаляете все эти файлы. Это может не иметь значения для небольших файлов, но когда файлы большого размера и количество файлов также велико, это может вызвать ненужные проблемы с производительностью. например, вы загружаете 100 файлов размером 10 МБ каждый, затем вы загружаете 1000 МБ, но выполнение вашего файла в базе данных показывает ошибку при первом обновлении базы данных, тогда загрузка оставшихся 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);
    
    }

другой способ, который вы можете сделать, это сохранить изображение во временной папке и переместить файл на постоянной основе только тогда, когда все файлы будут загружены, и как только произойдет какая-либо ошибка, вы удалите все файлы во временном каталоге, не удаляя их в цикле.

avatar
Martin Bean
1 июля 2021 в 20:50
0

Ваш контроллер должен получать только полностью проверенные данные. Таким образом, эти изображения должны быть готовы к загрузке, как только вы окажетесь в теле метода контроллера:

.
$paths = [];

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

// Do something with $paths

Вероятно, вы хотите загружать изображения асинхронно. Особенно если их пять в запросе. При таком подходе вы можете загружать файлы в папку, которая периодически очищается (скажем, через 24 часа). Когда изображение загружается в эту папку, верните пути. Отправьте пути к действию вашего контроллера вместо реальных файлов, после чего вы сможете переместить файлы из временной папки в постоянную.

Mustafa Reda
1 июля 2021 в 21:06
0

данные формы проверяются в FormRequest, но я не знаю, может ли store() или storeAs() произойти сбой, и что произойдет в этом случае, или если не удастся сохранить запросы. Так есть ли способ сделать их такими (все или нет)? или я преувеличиваю.