当前位置:Gxlcms > PHP教程 > Laravel5.1中基于Dropzone.js实现图片拖拽上传及删除功能

Laravel5.1中基于Dropzone.js实现图片拖拽上传及删除功能

时间:2021-07-01 10:21:17 帮助过:16人阅读

注:本教程代码适用于Laravel 5.1版本。

1、概述

Dropzone 是目前最好的免费文件拖拽上传库,它拥有很多特性和选项以便你可以使用多种方式来自定义。

在Laravel项目集成Dropzone对没有经验的新手来说有点棘手,所以接下来我想你给你们展示最优雅的解决方案。

这篇教程将包括以下内容:

  • 自动 图片 上传
  • 通过Ajax请求从Dropzone预览中直接移除图片
  • 上传图片计数器
  • 保存完整尺寸图片和图标尺寸版本
  • 使用Intervention Image包调整图片尺寸和编码
  • 保存图片数据到数据库
  • 在服务器端生成唯一的图片名称

如果你还需要上传或裁剪图片,请参考上一篇教程: 在 Laravel 5 中使用 jQuery 插件 Croppic + Intervention Image 实现图片上传和裁剪 。

本教程的完整代码已公开在GitHub上: https://github.com/codingo-me/dropzone-laravel-image-upload

最终演示效果图如下:

2、项目基本配置

这里我们安装一个新的Laravel应用并创建包含数据库凭证信息的.env文件。

此外我们还要从 https://github.com/enyo/dropzone/tree/master/dist 下载样式和脚本文件,这些文件存放到 public/packages/dropzone 。

前端我们还是使用Bootstrap,相关文件存放在 public/packages/bootstrap 。

上面已经提到我们要使用Intervention Image扩展包来处理上传图片,并且这里我们会在Blade模板中用到html辅助函数,所以我们需要安装相关依赖,安装Intervention Image请参考: 在 Laravel 5 中集成 Intervention Image 实现对图片的创建、修改和压缩处理 ,而安装html请参考: Laravel 5 中使用 HtmlBuilder 及 URL::asset() 引入站内或站外的 css 和 js 文件 。

3、视图

本教程只包含单个页面用于上传图片,尽管这样我还是喜欢将布局和具体实现页面分开,我们编辑布局文件 layout.blade.php 如下:

        Image upload in Laravel 5.1 with Dropzone.js        {!! HTML::style('/packages/bootstrap/css/bootstrap.min.css') !!}    {!! HTML::style('/assets/css/style.css') !!}    {!! HTML::script('https://code.jquery.com/jquery-2.1.4.min.js') !!}    @yield('head')                                    Dropzone + Laravel                                            

@yield('content')@yield('footer')

接下来我们创建上传图片的视图文件 pages/upload.blade.php :

@extends('layout')@section('head')    {!! HTML::style('/packages/dropzone/dropzone.css') !!}@stop@section('footer')    {!! HTML::script('/packages/dropzone/dropzone.js') !!}    {!! HTML::script('/assets/js/dropzone-config.js') !!}@stop@section('content')                                        

Images


{!! Form::open(['url' => route('upload-post'), 'class' => 'dropzone', 'files'=>true, 'id'=>'real-dropzone']) !!}

Drop images in this area

{!! Form::close() !!}
  • Images are uploaded as soon as you drop them
  • Maximum allowed size of image is 8MB
Check Created with Sketch. error Created with Sketch. @stop

在上传视图的 head 部分我们引入了Dropzone的默认样式,在 footer 部分我们传递默认的JavaScript文件以及我们自己的Dropzone配置文件。

内容部分包含两个部分,首先是上传表单然后是隐藏的图片预览区域,我是从Dropzone官网下载的预览模板代码,在配置文件中使用这个模板告诉Dropzone哪段HTML用于上传图片预览。

4、Dropzone配置

查看完整的Dropzone配置项列表参考 这里 ,对本项目来说我想从Dropzone一个一个上传文件,因为这样的话我就不需要循环遍历文件上传,并行上传文件数限制为100并且每个文件大小不能超过8M。

每个图片预览都对应一个删除链接,点击该链接Dropzone就会触发 removedfile 事件,该事件会创建上传/删除的Ajax请求,如果响应状态码是200,则对应图片计数器减少。

var photo_counter = 0;Dropzone.options.realDropzone = {    uploadMultiple: false,    parallelUploads: 100,    maxFilesize: 8,    previewsContainer: '#dropzonePreview',    previewTemplate: document.querySelector('#preview-template').innerHTML,    addRemoveLinks: true,    dictRemoveFile: 'Remove',    dictFileTooBig: 'Image is bigger than 8MB',    // The setting up of the dropzone    init:function() {        this.on("removedfile", function(file) {            $.ajax({                type: 'POST',                url: 'upload/delete',                data: {id: file.name},                dataType: 'html',                success: function(data){                    var rep = JSON.parse(data);                    if(rep.code == 200)                    {                        photo_counter--;                        $("#photoCounter").text( "(" + photo_counter + ")");                    }                }            });        } );    },    error: function(file, response) {        if($.type(response) === "string")            var message = response; //dropzone sends it's own error messages in string        else            var message = response.message;        file.previewElement.classList.add("dz-error");        _ref = file.previewElement.querySelectorAll("[data-dz-errormessage]");        _results = [];        for (_i = 0, _len = _ref.length; _i < _len; _i++) {            node = _ref[_i];            _results.push(node.textContent = message);        }        return _results;    },    success: function(file,done) {        photo_counter++;        $("#photoCounter").text( "(" + photo_counter + ")");    }}

该配置文件底部如果上传成功图片计数器增加。

5、上传逻辑

我习惯将特定逻辑放到Repository类,在本项目中我使用 ImageRepository 这个类,该类存放在 app/Logic/Image/ImageRepository.php 。

目前这个类只做两件事,上传单个文件以及删除指定文件,我们将整个Repository类注入到 ImageController 中:

image = $imageRepository;    }    public function getUpload()    {        return view('pages.upload');    }    public function postUpload()    {        $photo = Input::all();        $response = $this->image->upload($photo);        return $response;    }    public function deleteUpload()    {        $filename = Input::get('id');        if(!$filename)        {            return 0;        }        $response = $this->image->delete( $filename );        return $response;    }}

因此对这个控制器而言我需要三个路由:显示上传表单,上传以及删除请求:

Route::get('/', ['as' => 'upload', 'uses' => 'ImageController@getUpload']);Route::post('upload', ['as' => 'upload-post', 'uses' =>'ImageController@postUpload']);Route::post('upload/delete', ['as' => 'upload-remove', 'uses' =>'ImageController@deleteUpload']);

ImageController 的 postUpload 方法将用户上传请求转发到 ImageRepository 类对应的 upload 方法。这个 upload 方法首先会验证输入,然后为上传文件分配唯一的文件名,再然后保存原始尺寸图片到指定目录,同时将图标尺寸图片保存到其它目录,此外还要保存到数据库以便Laravel可以跟踪图片上传记录。为此我们需要创建迁移文件并运行迁移命令:

increments('id');            $table->text('original_name');            $table->text('filename');            $table->timestamps();        });    }    /**     * Reverse the migrations.     *     * @return void     */    public function down()    {        Schema::drop('images');    }}

我已经提过了上传文件将会保存为两种尺寸,此外可能还要生成缩略图并对图片编码,要实现这些功能,我们需要使用Intervention Image扩展包:

fails()) {            return Response::json([                'error' => true,                'message' => $validator->messages()->first(),                'code' => 400            ], 400);        }        $photo = $form_data['file'];        $originalName = $photo->getClientOriginalName();        $originalNameWithoutExt = substr($originalName, 0, strlen($originalName) - 4);        $filename = $this->sanitize($originalNameWithoutExt);        $allowed_filename = $this->createUniqueFilename( $filename );        $filenameExt = $allowed_filename .'.jpg';        $uploadSuccess1 = $this->original( $photo, $filenameExt );        $uploadSuccess2 = $this->icon( $photo, $filenameExt );        if( !$uploadSuccess1 || !$uploadSuccess2 ) {            return Response::json([                'error' => true,                'message' => 'Server error while uploading',                'code' => 500            ], 500);        }        $sessionImage = new Image;        $sessionImage->filename      = $allowed_filename;        $sessionImage->original_name = $originalName;        $sessionImage->save();        return Response::json([            'error' => false,            'code'  => 200        ], 200);    }    public function createUniqueFilename( $filename )    {        $full_size_dir = Config::get('images.full_size');        $full_image_path = $full_size_dir . $filename . '.jpg';        if ( File::exists( $full_image_path ) )        {            // Generate token for image            $imageToken = substr(sha1(mt_rand()), 0, 5);            return $filename . '-' . $imageToken;        }        return $filename;    }    /**     * Optimize Original Image     */    public function original( $photo, $filename )    {        $manager = new ImageManager();        $image = $manager->make( $photo )->encode('jpg')->save(Config::get('images.full_size') . $filename );        return $image;    }    /**     * Create Icon From Original     */    public function icon( $photo, $filename )    {        $manager = new ImageManager();        $image = $manager->make( $photo )->encode('jpg')->resize(200, null, function($constraint){$constraint->aspectRatio();})->save( Config::get('images.icon_size')  . $filename );        return $image;    }    /**     * Delete Image From Session folder, based on original filename     */    public function delete( $originalFilename)    {        $full_size_dir = Config::get('images.full_size');        $icon_size_dir = Config::get('images.icon_size');        $sessionImage = Image::where('original_name', 'like', $originalFilename)->first();        if(empty($sessionImage))        {            return Response::json([                'error' => true,                'code'  => 400            ], 400);        }        $full_path1 = $full_size_dir . $sessionImage->filename . '.jpg';        $full_path2 = $icon_size_dir . $sessionImage->filename . '.jpg';        if ( File::exists( $full_path1 ) )        {            File::delete( $full_path1 );        }        if ( File::exists( $full_path2 ) )        {            File::delete( $full_path2 );        }        if( !empty($sessionImage))        {            $sessionImage->delete();        }        return Response::json([            'error' => false,            'code'  => 200        ], 200);    }    function sanitize($string, $force_lowercase = true, $anal = false)    {        $strip = array("~", "`", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "=", "+", "[", "{", "]",            "}", "\\", "|", ";", ":", "\"", "'", "‘", "’", "“", "”", "–", "—",            "—", "–", ",", "<", ".", ">", "/", "?");        $clean = trim(str_replace($strip, "", strip_tags($string)));        $clean = preg_replace('/\s+/', "-", $clean);        $clean = ($anal) ? preg_replace("/[^a-zA-Z0-9]/", "", $clean) : $clean ;        return ($force_lowercase) ?            (function_exists('mb_strtolower')) ?                mb_strtolower($clean, 'UTF-8') :                strtolower($clean) :            $clean;    }}

上传方法会验证 Image 模型中指定的字段规则,目前该规则只是限定上传图片的扩展。

在 upload 方法之后我们使用 reateUniqueFilename 方法为图片返回唯一文件名。

当唯一文件名返回后,我们将图片和对应文件名传递到 original 方法,该方法负责保存完整尺寸图片,对小尺寸图标图片我们使用 icon 方法进行处理。

这两个尺寸图片都保存好了之后,系统会在图片表 images 中创建一条新纪录。

你可能已经注意到了我将图片上传目录存放在了配置文件中,当然你可以自定义存放目录或者直接使用Laravel内置的方法。

 env('UPLOAD_FULL_SIZE'),    'icon_size'   => env('UPLOAD_ICON_SIZE'),];

要让应用正常工作还需要将 .env.example 文件拷贝到 .env 文件,并且设置相应的配置值。注意到最后两个配置项的上传目录需要以 / 结尾,以我本地配置为例:

UPLOAD_FULL_SIZE=F:/xampp/htdocs/nonfu/dropzone-laravel-image-upload/public/images/full_size/UPLOAD_ICON_SIZE=F:/xampp/htdocs/nonfu/dropzone-laravel-image-upload/public/images/icon_size/

删除方法接收文件名并检查数据库中是否包含,然后在对应文件在是否存在于指定目录,如果两项都符合则该图片会被删除。

想要更加轻松地使用DropzoneJS实现图片上传,可以使用Laravel扩展包 Dropzoner 。

声明:本文为译文,原文: https://tuts.codingo.me/laravel-5-1-and-dropzone-js-auto-image-uploads-with-removal-links

人气教程排行