将区域表单拖放到全局表单中:无法使用 Laravel 获取文件
Dropzone form into a global form : impossible to get the file with Laravel
我正在尝试在我网站的主页上创建一个表单,其中包含一个带有预览的拖放区区域,以查看具有其他三个输入的文件。目标是在验证时,所有内容都存储在数据库中,并且我能够在另一页上打开文档。
问题是,无论我尝试什么,一旦文件加载 Dropzone.JS,我似乎无法在服务器端获取文件 Dropzone.JS。
每次在服务器端我都能获得我的表单数据,但不能获得文件本身,这会在服务器(控制器)端给我一个错误。
此外,还有一个问题是进度条是空的,提交表单后甚至没有填写。最好是它加载文件(将进度条设置为 100%),然后一旦它 "loaded",我就可以将它与我的表单的其余部分一起提交。
我正在使用 Bootstrap 实现提供的部分代码:dropzone-bootstrap.
这是我的 HTML 表格:
<form method="post" action="/upload-free-document" id="send-files-form" files="true" enctype="multipart/form-data">
<h3 class="form-title text-left">@lang('landing.get_started')</h3>
{{ csrf_field() }}
<div class="form-header">
<div class="form-group icon-addon addon-lg">
<input type="text" name="email" id="email" class="form-control wow fadeInUp" placeholder="@lang('landing.your_email')" required>
<label for="email" class="glyphicon glyphicon-user wow fadeInUp" rel="tooltip" title="@lang('landing.your_email')"></label>
</div>
<div class="form-group icon-addon addon-lg">
<input type="text" name="signatories-email" id="signatories-email" class="form-control wow fadeInUp" placeholder="@lang('landing.signatories_email')" required>
<label for="email" class="glyphicon glyphicon-envelope wow fadeInUp" rel="tooltip" title="@lang('landing.your_email')"></label>
</div>
<div class="form-group">
<div class="col-md-12 input-group">
<span class="input-group-addon message-icon wow fadeInUp"><span class="glyphicon glyphicon-pencil message-glyph-icon"></span></span>
<textarea id="document-message" name="document-message" rows="10" cols="20" class="form-control input-message wow fadeInUp" maxlength="1500" placeholder="@lang('landing.your_message')" required></textarea>
</div>
<span class="caracters-left"><span id="chars">1500</span> @lang('landing.caracters_left')</span>
</div>
<div id="actions" class="row">
<div class="col-lg-6">
<span class="btn btn-success fileinput-button upload-buttons add-file-button">
<i class="glyphicon glyphicon-plus"></i>
<span>@lang('landing.add_file')</span>
</span>
</div>
</div>
<div class="table table-striped files" id="previews">
<div id="template" class="file-row">
<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>
</div>
<div class="form-group last">
<input type="submit" id="submit-document" class="btn btn-warning btn-block btn-lg" value="@lang('landing.demo_sign_document')">
</div>
<p class="privacy text-center">@lang('landing.privacy_text') <a href="privacy.html">@lang('landing.privacy_link')</a>.</p>
</div>
这里是 Dropzone 配置:
// Get the template HTML and remove it from the doumenthe template HTML and remove it from the doument
var previewNode = document.querySelector("#template");
previewNode.id = "";
var previewTemplate = previewNode.parentNode.innerHTML;
previewNode.parentNode.removeChild(previewNode);
Dropzone.autoDiscover = false;
var myDropzone = new Dropzone(document.body, { // Make the whole body a dropzone
url: "/upload-free-document",
thumbnailWidth: 80,
thumbnailHeight: 80,
parallelUploads: 20,
previewTemplate: previewTemplate,
autoQueue: false,
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.
maxFiles: 1,
acceptedFiles: ".pdf", //is this correct? I got an error if im using this
maxFilesize: 3145728,
parallelUploads: 1,
uploadMultiple: false,
autoProcessQueue: false,
// The setting up of the dropzone
init: function() {
var myDropzone = this;
$("#submit-document").click(function (e) {
// e.preventDefault();
e.stopPropagation();
myDropzone.processQueue();
});
myDropzone.on("addedfile", function(file) {
console.log("Fichier ajouté");
});
// Listen to the sendingmultiple event. In this case, it's the sendingmultiple event instead
// of the sending event because uploadMultiple is set to true.
this.on("sending", function(file, xhr, formData) {
console.log(formData);
formData.append("email", $('#email').val());
formData.append("_token", $('[name=_token').val());
console.log("Fichier en cours d'envoi.");
});
this.on("success", function(files, response) {
// Gets triggered when the files have successfully been sent.
// Redirect user or notify of success
myDropzone.removeAllFiles();
console.log("Succès de l'envoi.");
});
this.on("error", function(files, response) {
// Gets triggered when there was an error sending the files.
// Maybe show form again, and notify user of error
console.log("Erreur de l'envoi.");
});
}
});
这是上传文件的方法(该方法在另一个页面上运行,我只有 DropzoneJS 部分,而不是包含 Dropzone.JS 的整个表单)。
/**
* Upload the user file to the server
*
* @param \Illuminate\Http\Request $request The request
*
* @return string ( response message )
*/
public function upload(Request $request)
{
$userFiles = null;
$email = $request->input('email');
// Check if there is already a file associated with the email
if ($email)
{
$userFiles = Files::getNonUserFiles($email);
}
else
{
return Response::json(array(
'error' => Lang::get('landing.no_email')
), 400);
}
// If the non user already has a file online, we block it
if(!$userFiles->isEmpty())
{
return Response::json(array(
'error' => Lang::get('landing.already_have_file')
), 400);
}
// Upload settings
$uploadSettings = uploadSettings::first();
// If there is a file uploaded
if ($request->file('file')->isValid())
{
// Getting the uploaded file
$file = $request->file('file');
// Get the extension of the file
$extension = $file->getClientOriginalExtension();
// Getting allowed extension
$allowedExt = explode(',' , $uploadSettings->allowedFilesExt);
// Get the file size
$size = $file->getSize();
// File size
if($size > $uploadSettings->maxFreeFileSize)
{
return Response::json(array(
'error' => Lang::get('documents.size_too_big')
), 400);
}
// File type
else if( !in_array(strtolower($extension), $allowedExt) )
{
return Response::json(array(
'error' => Lang::get('documents.wrong_format')
), 400);
}
// If everything is all right
else
{
// Where the file will be uploaded
$destinationPath = $this->noUserStorageService->storageDirectory();
// Generate a new date
$date = time('d-m-Y h:i:s.u');
if (!preg_match('/^[\x20-\x7E]+$/', $file->getClientOriginalName() ))
{
$filename = str_replace(' ','',$date.'_'.generateRandomString(10).'.'.$file->getClientOriginalExtension());
}
else
{
$filename = str_replace(' ','',$date.'_'.$file->getClientOriginalName());
$filename = str_replace("#", "_", $filename);
}
$path = $request->file('file')->storeAs(
$destinationPath, $filename
);
// If file Uploaded Success
if ($path)
{
// We create the file into the database
$files = new Files;
// File Name
$files->name = pathinfo(strtolower(htmlentities($file->getClientOriginalName())), PATHINFO_FILENAME);
$files->server_name = $filename;
// File Path
$files->path = preg_replace('/\s+/', '',url('/file/'.pathinfo($filename,PATHINFO_FILENAME)));
// File Extention
$files->extension = $extension;
$files->user_email = $email;
// File Status
$files->status = 1;
$files->auto_sign = 0;
$files->size = $file->getClientSize() ;
// Save File Info
$files->save();
return Response::json(array(
'message' => 'success'
), 200);
}
else
{
return Response::json(array(
'error' => "Impossible d'envoyer le fichier sur le serveur. Veuillez réessayer."
), 400);
}
}
}
}
我不认为它来自我的 Laravel 配置,因为 DropzoneJS 经典形式(未包含在另一种形式中)在我项目的其他地方工作正常。
我的表单或放置区配置有什么问题?
我一直在玩这个。 Bootstrap 演示对香草 Dropzone 进行的定制之一是每个单独文件的开始按钮。但是,您没有使用该开始按钮(您的模板不包括一个)。此外,您还有 maxFiles: 1
和 parallelUploads: 1
(实际上您也有 parallelUploads: 20
,但第二个可能优先)。所以看起来您真的只想在此表单上上传 1 个文件,对吗?在这种情况下,为什么需要这个 Bootstrap 演示方法?如果目标是调整文件的布局和外观并上传,您可以使用 previewTemplate
在 vanilla Dropzone 中完成,这更简单一些,我认为可以避免您看到的问题。
无论如何,问题的关键是在 Boostrap 演示中,开始按钮是将文件排入队列的按钮。他们的配置有 autoQueue: false
,因此没有文件自动排队。相反,他们在每次添加文件时添加一个事件监听器:
file.previewElement.querySelector(".start").onclick = function() { myDropzone.enqueueFile(file); };
您也有 autoQueue: false
,但您没有开始按钮,也没有手动将文件排队,因此您的文件永远不会排队。因此,当您点击提交时,Dropzone 的队列中没有任何要处理的内容,因此您在服务器端看到的只是其他表单输入值(电子邮件等)。
最简单的修复方法就是删除 autoQueue: false
,这样您的文件将在 select 后立即加入队列。在对您的代码进行本地测试时,这对我有用 - 该文件包含在 POSTed 到后端的数据中。
需要注意的一件事是,当您点击提交按钮时,实际上会发生 2 个单独的 POST。首先 Dropzone 只发布文件,然后您的表单与您的文本输入一起提交。这将是另一个问题,因为看起来您的控制器目前希望同时上传表单数据和文件。
看起来您可能已经开始解决这个问题了,方法是 formData.append
将您的一些表单输入输入到 Dropzone POST。您可以为每个输入执行此操作,然后以某种方式忽略第二个 POST(或者让 action
指向不同的 Controller 方法?)。或者,您可以为每个 POST 使用单独的控制器方法 - 使用 form
的 action
指定保存表单数据的方法,并使用 Dropzone 的 url
配置选项指定处理文件上传的不同方法。
更新
这里有一个更清晰的示例,说明了解决这个双重 POST 问题的方法。
在您的 Javascript 中,首先将表单中的每个输入附加到 Dropzone:
this.on("sending", function(file, xhr, formData) {
// Get every input on the form
var data = $('#send-files-form').serializeArray();
// Append them all to the formData Dropzone will POST
$.each(data, function(key, el) {
formData.append(el.name, el.value);
});
console.log("Fichier en cours d'envoi.");
});
现在您的所有表单数据以及文件都在 Dropzone 将执行的 POST 中。这意味着您可以忽略或禁用 Dropzone 完成后发生的第二个 POST。为此,请停止实际发布的表格:
$("#submit-document").click(function (e) {
e.preventDefault();
myDropzone.processQueue();
});
现在只有 1 个 POST。当然,现在您必须手动处理表单提交后的操作。您可以使用 success
和 error
Dropzone 回调来做到这一点。您的控制器已经返回 JSON 成功或错误消息,因此也许您可以在前端显示它。在 HTML:
中添加某种消息占位符
<div class="message"></div>
并在您的回调中定位它:
this.on("success", function(files, response) {
$('div.message').html(response.message);
myDropzone.removeAllFiles();
console.log("Succès de l'envoi.");
});
this.on("error", function(files, response) {
$('div.message').html(response.error);
console.log("Erreur de l'envoi.");
});
或者在成功提交后您可能想重定向到另一个页面:
this.on("success", function(files, response) {
myDropzone.removeAllFiles();
window.location('/some/other/place');
console.log("Succès de l'envoi.");
});
小问题 - 您包含的代码缺少结束 </form>
标记,这让我有些头疼。您可能只是没有从您的代码中复制粘贴它,而是为了以防万一提及它。
最后一件事 - the docs seem to be broken on this,但是 AFAICT maxFilesize
值应该以 MB 为单位(在文档中搜索该字符串以查看一些示例)。您的 maxFilesize: 3145728
值可能应该类似于 maxFilesize: 3
.
我正在尝试在我网站的主页上创建一个表单,其中包含一个带有预览的拖放区区域,以查看具有其他三个输入的文件。目标是在验证时,所有内容都存储在数据库中,并且我能够在另一页上打开文档。
问题是,无论我尝试什么,一旦文件加载 Dropzone.JS,我似乎无法在服务器端获取文件 Dropzone.JS。
每次在服务器端我都能获得我的表单数据,但不能获得文件本身,这会在服务器(控制器)端给我一个错误。
此外,还有一个问题是进度条是空的,提交表单后甚至没有填写。最好是它加载文件(将进度条设置为 100%),然后一旦它 "loaded",我就可以将它与我的表单的其余部分一起提交。
我正在使用 Bootstrap 实现提供的部分代码:dropzone-bootstrap.
这是我的 HTML 表格:
<form method="post" action="/upload-free-document" id="send-files-form" files="true" enctype="multipart/form-data">
<h3 class="form-title text-left">@lang('landing.get_started')</h3>
{{ csrf_field() }}
<div class="form-header">
<div class="form-group icon-addon addon-lg">
<input type="text" name="email" id="email" class="form-control wow fadeInUp" placeholder="@lang('landing.your_email')" required>
<label for="email" class="glyphicon glyphicon-user wow fadeInUp" rel="tooltip" title="@lang('landing.your_email')"></label>
</div>
<div class="form-group icon-addon addon-lg">
<input type="text" name="signatories-email" id="signatories-email" class="form-control wow fadeInUp" placeholder="@lang('landing.signatories_email')" required>
<label for="email" class="glyphicon glyphicon-envelope wow fadeInUp" rel="tooltip" title="@lang('landing.your_email')"></label>
</div>
<div class="form-group">
<div class="col-md-12 input-group">
<span class="input-group-addon message-icon wow fadeInUp"><span class="glyphicon glyphicon-pencil message-glyph-icon"></span></span>
<textarea id="document-message" name="document-message" rows="10" cols="20" class="form-control input-message wow fadeInUp" maxlength="1500" placeholder="@lang('landing.your_message')" required></textarea>
</div>
<span class="caracters-left"><span id="chars">1500</span> @lang('landing.caracters_left')</span>
</div>
<div id="actions" class="row">
<div class="col-lg-6">
<span class="btn btn-success fileinput-button upload-buttons add-file-button">
<i class="glyphicon glyphicon-plus"></i>
<span>@lang('landing.add_file')</span>
</span>
</div>
</div>
<div class="table table-striped files" id="previews">
<div id="template" class="file-row">
<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>
</div>
<div class="form-group last">
<input type="submit" id="submit-document" class="btn btn-warning btn-block btn-lg" value="@lang('landing.demo_sign_document')">
</div>
<p class="privacy text-center">@lang('landing.privacy_text') <a href="privacy.html">@lang('landing.privacy_link')</a>.</p>
</div>
这里是 Dropzone 配置:
// Get the template HTML and remove it from the doumenthe template HTML and remove it from the doument
var previewNode = document.querySelector("#template");
previewNode.id = "";
var previewTemplate = previewNode.parentNode.innerHTML;
previewNode.parentNode.removeChild(previewNode);
Dropzone.autoDiscover = false;
var myDropzone = new Dropzone(document.body, { // Make the whole body a dropzone
url: "/upload-free-document",
thumbnailWidth: 80,
thumbnailHeight: 80,
parallelUploads: 20,
previewTemplate: previewTemplate,
autoQueue: false,
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.
maxFiles: 1,
acceptedFiles: ".pdf", //is this correct? I got an error if im using this
maxFilesize: 3145728,
parallelUploads: 1,
uploadMultiple: false,
autoProcessQueue: false,
// The setting up of the dropzone
init: function() {
var myDropzone = this;
$("#submit-document").click(function (e) {
// e.preventDefault();
e.stopPropagation();
myDropzone.processQueue();
});
myDropzone.on("addedfile", function(file) {
console.log("Fichier ajouté");
});
// Listen to the sendingmultiple event. In this case, it's the sendingmultiple event instead
// of the sending event because uploadMultiple is set to true.
this.on("sending", function(file, xhr, formData) {
console.log(formData);
formData.append("email", $('#email').val());
formData.append("_token", $('[name=_token').val());
console.log("Fichier en cours d'envoi.");
});
this.on("success", function(files, response) {
// Gets triggered when the files have successfully been sent.
// Redirect user or notify of success
myDropzone.removeAllFiles();
console.log("Succès de l'envoi.");
});
this.on("error", function(files, response) {
// Gets triggered when there was an error sending the files.
// Maybe show form again, and notify user of error
console.log("Erreur de l'envoi.");
});
}
});
这是上传文件的方法(该方法在另一个页面上运行,我只有 DropzoneJS 部分,而不是包含 Dropzone.JS 的整个表单)。
/**
* Upload the user file to the server
*
* @param \Illuminate\Http\Request $request The request
*
* @return string ( response message )
*/
public function upload(Request $request)
{
$userFiles = null;
$email = $request->input('email');
// Check if there is already a file associated with the email
if ($email)
{
$userFiles = Files::getNonUserFiles($email);
}
else
{
return Response::json(array(
'error' => Lang::get('landing.no_email')
), 400);
}
// If the non user already has a file online, we block it
if(!$userFiles->isEmpty())
{
return Response::json(array(
'error' => Lang::get('landing.already_have_file')
), 400);
}
// Upload settings
$uploadSettings = uploadSettings::first();
// If there is a file uploaded
if ($request->file('file')->isValid())
{
// Getting the uploaded file
$file = $request->file('file');
// Get the extension of the file
$extension = $file->getClientOriginalExtension();
// Getting allowed extension
$allowedExt = explode(',' , $uploadSettings->allowedFilesExt);
// Get the file size
$size = $file->getSize();
// File size
if($size > $uploadSettings->maxFreeFileSize)
{
return Response::json(array(
'error' => Lang::get('documents.size_too_big')
), 400);
}
// File type
else if( !in_array(strtolower($extension), $allowedExt) )
{
return Response::json(array(
'error' => Lang::get('documents.wrong_format')
), 400);
}
// If everything is all right
else
{
// Where the file will be uploaded
$destinationPath = $this->noUserStorageService->storageDirectory();
// Generate a new date
$date = time('d-m-Y h:i:s.u');
if (!preg_match('/^[\x20-\x7E]+$/', $file->getClientOriginalName() ))
{
$filename = str_replace(' ','',$date.'_'.generateRandomString(10).'.'.$file->getClientOriginalExtension());
}
else
{
$filename = str_replace(' ','',$date.'_'.$file->getClientOriginalName());
$filename = str_replace("#", "_", $filename);
}
$path = $request->file('file')->storeAs(
$destinationPath, $filename
);
// If file Uploaded Success
if ($path)
{
// We create the file into the database
$files = new Files;
// File Name
$files->name = pathinfo(strtolower(htmlentities($file->getClientOriginalName())), PATHINFO_FILENAME);
$files->server_name = $filename;
// File Path
$files->path = preg_replace('/\s+/', '',url('/file/'.pathinfo($filename,PATHINFO_FILENAME)));
// File Extention
$files->extension = $extension;
$files->user_email = $email;
// File Status
$files->status = 1;
$files->auto_sign = 0;
$files->size = $file->getClientSize() ;
// Save File Info
$files->save();
return Response::json(array(
'message' => 'success'
), 200);
}
else
{
return Response::json(array(
'error' => "Impossible d'envoyer le fichier sur le serveur. Veuillez réessayer."
), 400);
}
}
}
}
我不认为它来自我的 Laravel 配置,因为 DropzoneJS 经典形式(未包含在另一种形式中)在我项目的其他地方工作正常。
我的表单或放置区配置有什么问题?
我一直在玩这个。 Bootstrap 演示对香草 Dropzone 进行的定制之一是每个单独文件的开始按钮。但是,您没有使用该开始按钮(您的模板不包括一个)。此外,您还有 maxFiles: 1
和 parallelUploads: 1
(实际上您也有 parallelUploads: 20
,但第二个可能优先)。所以看起来您真的只想在此表单上上传 1 个文件,对吗?在这种情况下,为什么需要这个 Bootstrap 演示方法?如果目标是调整文件的布局和外观并上传,您可以使用 previewTemplate
在 vanilla Dropzone 中完成,这更简单一些,我认为可以避免您看到的问题。
无论如何,问题的关键是在 Boostrap 演示中,开始按钮是将文件排入队列的按钮。他们的配置有 autoQueue: false
,因此没有文件自动排队。相反,他们在每次添加文件时添加一个事件监听器:
file.previewElement.querySelector(".start").onclick = function() { myDropzone.enqueueFile(file); };
您也有 autoQueue: false
,但您没有开始按钮,也没有手动将文件排队,因此您的文件永远不会排队。因此,当您点击提交时,Dropzone 的队列中没有任何要处理的内容,因此您在服务器端看到的只是其他表单输入值(电子邮件等)。
最简单的修复方法就是删除 autoQueue: false
,这样您的文件将在 select 后立即加入队列。在对您的代码进行本地测试时,这对我有用 - 该文件包含在 POSTed 到后端的数据中。
需要注意的一件事是,当您点击提交按钮时,实际上会发生 2 个单独的 POST。首先 Dropzone 只发布文件,然后您的表单与您的文本输入一起提交。这将是另一个问题,因为看起来您的控制器目前希望同时上传表单数据和文件。
看起来您可能已经开始解决这个问题了,方法是 formData.append
将您的一些表单输入输入到 Dropzone POST。您可以为每个输入执行此操作,然后以某种方式忽略第二个 POST(或者让 action
指向不同的 Controller 方法?)。或者,您可以为每个 POST 使用单独的控制器方法 - 使用 form
的 action
指定保存表单数据的方法,并使用 Dropzone 的 url
配置选项指定处理文件上传的不同方法。
更新
这里有一个更清晰的示例,说明了解决这个双重 POST 问题的方法。
在您的 Javascript 中,首先将表单中的每个输入附加到 Dropzone:
this.on("sending", function(file, xhr, formData) {
// Get every input on the form
var data = $('#send-files-form').serializeArray();
// Append them all to the formData Dropzone will POST
$.each(data, function(key, el) {
formData.append(el.name, el.value);
});
console.log("Fichier en cours d'envoi.");
});
现在您的所有表单数据以及文件都在 Dropzone 将执行的 POST 中。这意味着您可以忽略或禁用 Dropzone 完成后发生的第二个 POST。为此,请停止实际发布的表格:
$("#submit-document").click(function (e) {
e.preventDefault();
myDropzone.processQueue();
});
现在只有 1 个 POST。当然,现在您必须手动处理表单提交后的操作。您可以使用 success
和 error
Dropzone 回调来做到这一点。您的控制器已经返回 JSON 成功或错误消息,因此也许您可以在前端显示它。在 HTML:
<div class="message"></div>
并在您的回调中定位它:
this.on("success", function(files, response) {
$('div.message').html(response.message);
myDropzone.removeAllFiles();
console.log("Succès de l'envoi.");
});
this.on("error", function(files, response) {
$('div.message').html(response.error);
console.log("Erreur de l'envoi.");
});
或者在成功提交后您可能想重定向到另一个页面:
this.on("success", function(files, response) {
myDropzone.removeAllFiles();
window.location('/some/other/place');
console.log("Succès de l'envoi.");
});
小问题 - 您包含的代码缺少结束 </form>
标记,这让我有些头疼。您可能只是没有从您的代码中复制粘贴它,而是为了以防万一提及它。
最后一件事 - the docs seem to be broken on this,但是 AFAICT maxFilesize
值应该以 MB 为单位(在文档中搜索该字符串以查看一些示例)。您的 maxFilesize: 3145728
值可能应该类似于 maxFilesize: 3
.