使用 Dropzone 和 Laravel 获取要上传的图像

Posted

技术标签:

【中文标题】使用 Dropzone 和 Laravel 获取要上传的图像【英文标题】:Getting an image to upload using Dropzone and Laravel 【发布时间】:2018-06-01 14:47:03 【问题描述】:

我正在尝试创建一个页面,用户可以在其中添加标题,然后上传一个或 2 个图像。我正在使用 dropzone 和 Laravel,我试图让它看起来不同, 我让它看起来像http://www.dropzonejs.com/bootstrap.html

我遇到的问题是我需要在 js 文件中添加一个 url,但它一直给我这个错误

POST http://crud.test/portfolios419(未知状态)

在我的开发工具中预览

消息:“”,异常:“Symfony\Component\HttpKernel\Exception\HttpException”,…

我知道在 Laravel 中我会使用

 csrf_field() 

我无法将图像上传到我在控制器中指定的文件夹或将图像保存到我的数据库中。

我想要的是如何让我的图像上传到文件夹并保存到我的数据库中。

我的刀片:

<form action=" route('portfolios.store') ">
     csrf_field() 

    <div class="form-group">
        <label>Title</label>
        <input type="title" name="title" class="form-control">
    </div>

    <div id="actions" class="row">
        <div class="col-lg-7">
            <span class="btn btn-success fileinput-button dz-clickable">
                <i class="glyphicon glyphicon-plus"></i>
                <span>Add files...</span>
            </span>

            <button type="button" class="btn btn-info start">
                <i class="glyphicon glyphicon-upload"></i>
                <span>Start upload</span>
            </button>

            <button type="reset" class="btn btn-warning cancel">
                <i class="glyphicon glyphicon-ban-circle"></i>
                <span>Cancel upload</span>
            </button>
        </div>

        <div class="col-lg-5">
            <span class="fileupload-process">
                <div id="total-progress" class="progress progress-striped active total-upload-progress" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0">
                    <div class="progress-bar progress-bar-success" style="width:0%;" data-dz-uploadprogress=""></div>
                </div>
            </span>
        </div>
    </div>

    <div class="table table-striped files" id="previews">
        <div id="template" class="file-row dz-image-preview">
            <div>
                <span class="preview">
                    <img data-dz-thumbnail />
                </span>
            </div>

            <div>
                <p class="name" data-dz-name></p>
                <strong class="error text-danger" data-dz-errormessage></strong>
            </div>

            <div>
                <p class="size" data-dz-size></p>
                <div class="progress progress-striped active" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0">
                    <div class="progress-bar progress-bar-success" style="width:0%;" data-dz-uploadprogress></div>
                </div>
            </div>

            <div>
                <button class="btn btn-info start">
                    <i class="glyphicon glyphicon-upload"></i>
                    <span>Start</span>
                </button>

                <button data-dz-remove class="btn btn-warning cancel">
                    <i class="glyphicon glyphicon-ban-circle"></i>
                    <span>Cancel</span>
                </button>

                <button data-dz-remove class="btn btn-danger delete">
                    <i class="glyphicon glyphicon-trash"></i>
                    <span>Delete</span>
                </button>
            </div>
        </div>
    </div>

    <button type="submit" class="btn btn-primary">Submit</button>
</form>

我的 main.js:

var previewNode = document.querySelector("#template");
previewNode.id = "";
var previewTemplate = previewNode.parentNode.innerHTML;
previewNode.parentNode.removeChild(previewNode);

var myDropzone = new Dropzone(document.body,  // Make the whole body a dropzone
    url: "/portfolios", // Set the url
    thumbnailWidth: 80,
    thumbnailHeight: 80,
    parallelUploads: 20,
    previewTemplate: previewTemplate,
    autoQueue: false, // Make sure the files aren't queued until manually added
    uploadMultiple: true,
    previewsContainer: "#previews", // Define the container to display the previews
    clickable: ".fileinput-button" // Define the element that should be used as click trigger to select files.
);

myDropzone.on("addedfile", function(file) 
    file.previewElement.querySelector(".start").onclick = function()  
        myDropzone.enqueueFile(file); 
    ;
);

myDropzone.on("totaluploadprogress", function(progress) 
    document.querySelector("#total-progress .progress-bar").style.width = progress + "%";
);

myDropzone.on("sending", function(file) 
    document.querySelector("#total-progress").style.opacity = "1";
    file.previewElement.querySelector(".start").setAttribute("disabled", "disabled");
);

myDropzone.on("queuecomplete", function(progress) 
    document.querySelector("#total-progress").style.opacity = "0";
);

document.querySelector("#actions .start").onclick = function() 
    myDropzone.enqueueFiles(myDropzone.getFilesWithStatus(Dropzone.ADDED));
;

document.querySelector("#actions .cancel").onclick = function() 
    myDropzone.removeAllFiles(true);
;

我的控制器:

public function store(Request $request)

    $portfolio = new Portfolio();

    $portfolio->fill($this->getSafeInput($request));

    if($request->hasFile('file'))
    
        $names = [];
        foreach($request->file('file') as $image)
        

            $destinationPath = 'portfolio_images/';
            $filename = $image->getClientOriginalName();
            $image->move($destinationPath, $filename);
            array_push($names, $filename); 
        

        $portfolio->file = json_encode($names);
    

    $portfolio->save();

    return redirect()->route('portfolios.index');

我的路线:

Route::get('/', function () 
    return view('welcome'); 
);

Auth::routes();

Route::get('/home', 'HomeController@index')->name('home');

Route::resource('/portfolios', 'PortfolioController');

我希望我已经正确解释了,如果我遗漏了任何信息,请告诉我

【问题讨论】:

【参考方案1】:

问题是 Dropzone 会在您删除文件时自动发布您的文件。但它在它发布的请求中不包含任何其他字段 - 只是文件。所以你的 CSRF 令牌丢失了(就像你表单上的所有其他字段一样),Laravel 会抛出一个错误。

有几种方法可以解决这个问题。也许最简单的方法是将 CSRF 令牌添加到您的 sending() even 处理程序中发布的数据中。 Dropzone docs even mention doing exactly this:

...您可以修改它们(例如添加 CSRF 令牌)...

所以,在你的代码中:

// First, include the 2nd and 3rd parameters passed to this handler
myDropzone.on("sending", function(file, xhr, formData) 

    // Now, find your CSRF token
    var token = $("input[name='_token']").val();

    // Append the token to the formData Dropzone is going to POST
    formData.append('_token', token);

    // Rest of your code ...
    document.querySelector("#total-progress").style.opacity = "1";
    file.previewElement.querySelector(".start").setAttribute("disabled", "disabled");
);

请注意,尽管您的 CSRF 令牌仍然在表单上未更改,但现在无效 - 刚刚处理的 POST 请求意味着它将已更新。您删除的下一个文件将重新使用相同的令牌,并且会失败。

更完整的解决方案是在您点击提交按钮之前完全禁用文件发送,然后一次发送所有文件和所有表单域。为此,您必须disable autoProcessQueue

autoProcessQueue = false,

接下来,由于队列不是自动处理的,您现在必须在提交表单时手动处理它,by firing processQueue()。提交表单时,您需要某种事件处理程序来触发:

// Event handler to fire when your form is submitted
$('form').on('submit', function(e) 
    // Don't let the standard form submit, we're doing it here
    e.preventDefault();

    // Process the file queue
    myDropzone.processQueue();
);

最后,您需要将您的 CSRF 令牌您表单中的任何其他字段添加到 Dropzone 将发布的数据中:

myDropzone.on("sending", function(file, xhr, formData) 

    // Find all input values on your form
    var data = $('form').serializeArray();

    // Append them all to the formData Dropzone will POST
    $.each(data, function(key, el) 
        formData.append(el.name, el.value);
    );

    // Rest of your code ...
    document.querySelector("#total-progress").style.opacity = "1";
    file.previewElement.querySelector(".start").setAttribute("disabled", "disabled");
);

【讨论】:

我已经添加了令牌,我已经添加了var formData = new FormData();,但仍然是同样的错误 @user3095193 我没有建议? 如果我没有添加它,那么我会得到一个formData not defined @user3095193 我想你错过了我写的东西 - // First, include the 2nd and 3rd parameters passed to this handler - 你需要添加 xhrformData 作为参数,正如我展示和描述的那样。 我确实错过了你写的东西。非常感谢您的回答是正确的,并帮助了我。【参考方案2】:

您的表单缺少文件上传的 enctype,enctype 属性指定表单数据在提交到服务器时应如何编码。所以将该属性添加到您的表单中:

&lt;form action="/action_page_binary.asp" method="post" enctype="multipart/form-data"&gt;

adn 也将方法指定为 post !

【讨论】:

我已经这样做了,但我仍然遇到同样的错误

以上是关于使用 Dropzone 和 Laravel 获取要上传的图像的主要内容,如果未能解决你的问题,请参考以下文章

使用 dropzone.js 和 laravel 5 验证 CsrfToken

如何克服 Laravel dropzone 文件上传中的 getClientOriginalName() 错误?

Dropzone CSRF令牌不匹配Laravel 5

在 laravel 中解码和移动 base64 编码的图像

如何在 Dropzone 上传请求的标头中包含 CSRF 令牌?

Dropzone多个文件上传不适用于Excel文件