没有AJAX的拖放文件上传,在前台同步?

时间:2021-10-31 15:04:58

I have a web site with a regular <input type="file"> file upload, POSTing the data to the backend when the form is submitted.

我有一个带有常规文件上传的网站,在提交表单时将数据发布到后端。

I would like to progressively enhance the form so that you can drop a file from outside the browser anywhere in the viewport (not just on the file input field, as built into some browsers) to upload it.

我想逐步增强表单,以便您可以从浏览器外的任何位置删除文件(不仅仅是在文件输入字段中,内置到某些浏览器中)来上传它。

Whether or not the form autosubmits isn't important. So if the drag-and-drop only selects the file in the file field, without starting an upload, that's fine. I don't need support for multiple files. I don't need to show upload progress, thumbnails or anything fancy.

表单自动提交是否不重要。因此,如果拖放只选择文件字段中的文件,而不开始上传,那就没问题。我不需要支持多个文件。我不需要显示上传进度,缩略图或任何花哨的东西。

I know there are JS libs that support drag-and-drop uploads, but they all seem to upload via AJAX. I could do that, but then I would need to modify the backend and frontend to handle upload errors, redirect and show the right messages on success and so on.

我知道有JS库支持拖放上传,但它们似乎都是通过AJAX上传的。我可以这样做,但后来我需要修改后端和前端来处理上传错误,重定向并在成功时显示正确的消息,等等。

I want a progressive enhancement that doesn't require any backend changes. It should happen synchronously using the form in the page. JS is fine, as long as the upload happens "in the foreground". Synchronous AJAX would not work, of course.

我想要一个不需要任何后端更改的渐进增强功能。它应该使用页面中的表单同步发生。 JS很好,只要上传发生在“前台”。当然,同步AJAX不起作用。

3 个解决方案

#1


17  

Although not really "synchronous" (JavaScript execution won't actually halt), you can set the files selected by <input type="file"> programatically. In fact, such elements and dragging share their file backend implementation (File and FileList instances), so it's really straight-forward. What's more, due to both frontends using FileLists, dragging multiple files work just as seamlessly.

虽然不是真正的“同步”(JavaScript执行实际上不会停止),但您可以通过编程方式设置选择的文件。实际上,这些元素和拖动共享它们的文件后端实现(File和FileList实例),所以它非常简单。更重要的是,由于前端使用FileLists,拖动多个文件的工作方式同样无缝。

This works in Chrome (using jQuery): http://jsfiddle.net/qMmPr/.

这适用于Chrome(使用jQuery):http://jsfiddle.net/qMmPr/。

$(document).on("dragover drop", function(e) {
    e.preventDefault();  // allow dropping and don't navigate to file on drop
}).on("drop", function(e) {
    $("input[type='file']")
        .prop("files", e.originalEvent.dataTransfer.files)  // put files into element
        .closest("form")
          .submit();  // autosubmit as well
});

#2


0  

Thanks to @pimvdb comment, I came up with a pretty elegant solution.

感谢@pimvdb评论,我提出了一个非常优雅的解决方案。

Since drag and dropping on the <input type="file" /> works, why not making it full-screen on dragstart to make sure the user can't miss it? Anyway he is dragging so his intentions are clear at this moment.

由于上的拖放工作,为什么不在dragstart上全屏显示以确保用户不会错过它?无论如何,他正在拖延,所以此刻他的意图很明确。

Here's a demo: https://jsfiddle.net/08wbo4um

这是一个演示:https://jsfiddle.net/08wbo4um

NB: unfortunately this doesn't seem to work in an iframe, but it does work on an actual page. You can still apprehend the behavior.

注意:遗憾的是,这似乎不适用于iframe,但它确实适用于实际页面。你仍然可以理解这种行为。

Here's the snippet:

这是片段:

  $('input[type="file"]').on('change', function(e){
    var fileName = e.target.files[0].name;
    if (fileName) {
      $(e.target).parent().attr('data-message', fileName);
    }
  });
  
  $(document).on('drag dragstart dragend dragover dragenter dragleave drop', function(e) {
    if ($('input[type="file"]').length) {
      if (['dragover', 'dragenter'].indexOf(e.type) > -1) {
        if (window.dragTimeout)
          clearTimeout(window.dragTimeout);
        $('body').addClass('dragged');
      } else if (['dragleave', 'drop'].indexOf(e.type) > -1) {
        // Without the timeout, some dragleave events are triggered
        // when the :after appears, making it blink...
        window.dragTimeout = setTimeout(function() {
          $('body').removeClass('dragged');
        }, 100);
      }
    }
  });
h3, p {
  text-align: center;
}

.form-group {
  margin: 30px;
}

.file-upload .form-control {
  height: 150px;
  outline: 1px dashed #ccc;
  outline-offset: -15px;
  background-color: #eee;
}
.file-upload .form-control:before {
  content: "\f093";
  font: normal normal normal 14px/1 FontAwesome;
  font-size: 3em;
  left: 0;
  right: 0;
  display: block;
  margin: 20px auto;
  text-align: center;
}
.file-upload .form-control:after {
  content: attr(data-message);
  left: 0;
  right: 0;
  bottom: 0;
  text-align: center;
  display: block;
}
.file-upload .form-control input[type="file"] {
  cursor: pointer;
  opacity: 0;
  width: 100%;
  height: 100%;
  position: absolute;
  top: 0;
  bottom: 0;
  right: 0;
  left: 0;
}
body.dragged .file-upload .form-control input[type="file"] {
  /* Make sure it is full screen, whatever the position absolute container */
  position: fixed;
  top: -50vh;
  bottom: -50vh;
  left: -50vw;
  right: -50vw;
  height: 200vh;
  width: 200vw;
  z-index: 10002;
}

body:after {
  content: 'You can drop the file. :-)';
  font-size: 2em;
  text-align: center;
  line-height: 100vh;
  position: absolute;
  top: 10px;
  bottom: 10px;
  left: 10px;
  right: 10px;
  background-color: #eee;
  z-index: 10000;
  border-radius: 4px;
  border: thin solid #ccc;
  visibility: hidden;
  opacity: 0;
  transition: visibility 0s, opacity 0.5s ease;
}

body.dragged:after {
  opacity: 1;
  visibility: visible;
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>

<h3>Drag N Drop file upload without AJAX Demo</h3>
<p>Try drag and dropping a file. :-)</p>

<div class="form-group file-upload" required="required">
    <label class="cols-sm-2 control-label" for="document_file">File Upload</label><br>
    <div class="cols-sm-10">
      <div class="input-group">
        <span class="input-group-addon"><i class="fa fa-file" aria-hidden="true"></i></span>
        <div class="form-control" data-message="Click to select file or drag n drop it here">
          <input required="required" title="Click to select file or drag n drop it here" type="file" name="document[file]" id="document_file">
        </div>
      </div>
    </div>
  </div>

#3


-1  

It can be done by turning autoUpload to false, collecting the files in an array, then on form submit do a single ajax call with all the files together with the form data, as described here.

可以通过将autoUpload设置为false,收集数组中的文件,然后在表单提交上执行单个ajax调用,将所有文件与表单数据一起执行,如此处所述。

#1


17  

Although not really "synchronous" (JavaScript execution won't actually halt), you can set the files selected by <input type="file"> programatically. In fact, such elements and dragging share their file backend implementation (File and FileList instances), so it's really straight-forward. What's more, due to both frontends using FileLists, dragging multiple files work just as seamlessly.

虽然不是真正的“同步”(JavaScript执行实际上不会停止),但您可以通过编程方式设置选择的文件。实际上,这些元素和拖动共享它们的文件后端实现(File和FileList实例),所以它非常简单。更重要的是,由于前端使用FileLists,拖动多个文件的工作方式同样无缝。

This works in Chrome (using jQuery): http://jsfiddle.net/qMmPr/.

这适用于Chrome(使用jQuery):http://jsfiddle.net/qMmPr/。

$(document).on("dragover drop", function(e) {
    e.preventDefault();  // allow dropping and don't navigate to file on drop
}).on("drop", function(e) {
    $("input[type='file']")
        .prop("files", e.originalEvent.dataTransfer.files)  // put files into element
        .closest("form")
          .submit();  // autosubmit as well
});

#2


0  

Thanks to @pimvdb comment, I came up with a pretty elegant solution.

感谢@pimvdb评论,我提出了一个非常优雅的解决方案。

Since drag and dropping on the <input type="file" /> works, why not making it full-screen on dragstart to make sure the user can't miss it? Anyway he is dragging so his intentions are clear at this moment.

由于上的拖放工作,为什么不在dragstart上全屏显示以确保用户不会错过它?无论如何,他正在拖延,所以此刻他的意图很明确。

Here's a demo: https://jsfiddle.net/08wbo4um

这是一个演示:https://jsfiddle.net/08wbo4um

NB: unfortunately this doesn't seem to work in an iframe, but it does work on an actual page. You can still apprehend the behavior.

注意:遗憾的是,这似乎不适用于iframe,但它确实适用于实际页面。你仍然可以理解这种行为。

Here's the snippet:

这是片段:

  $('input[type="file"]').on('change', function(e){
    var fileName = e.target.files[0].name;
    if (fileName) {
      $(e.target).parent().attr('data-message', fileName);
    }
  });
  
  $(document).on('drag dragstart dragend dragover dragenter dragleave drop', function(e) {
    if ($('input[type="file"]').length) {
      if (['dragover', 'dragenter'].indexOf(e.type) > -1) {
        if (window.dragTimeout)
          clearTimeout(window.dragTimeout);
        $('body').addClass('dragged');
      } else if (['dragleave', 'drop'].indexOf(e.type) > -1) {
        // Without the timeout, some dragleave events are triggered
        // when the :after appears, making it blink...
        window.dragTimeout = setTimeout(function() {
          $('body').removeClass('dragged');
        }, 100);
      }
    }
  });
h3, p {
  text-align: center;
}

.form-group {
  margin: 30px;
}

.file-upload .form-control {
  height: 150px;
  outline: 1px dashed #ccc;
  outline-offset: -15px;
  background-color: #eee;
}
.file-upload .form-control:before {
  content: "\f093";
  font: normal normal normal 14px/1 FontAwesome;
  font-size: 3em;
  left: 0;
  right: 0;
  display: block;
  margin: 20px auto;
  text-align: center;
}
.file-upload .form-control:after {
  content: attr(data-message);
  left: 0;
  right: 0;
  bottom: 0;
  text-align: center;
  display: block;
}
.file-upload .form-control input[type="file"] {
  cursor: pointer;
  opacity: 0;
  width: 100%;
  height: 100%;
  position: absolute;
  top: 0;
  bottom: 0;
  right: 0;
  left: 0;
}
body.dragged .file-upload .form-control input[type="file"] {
  /* Make sure it is full screen, whatever the position absolute container */
  position: fixed;
  top: -50vh;
  bottom: -50vh;
  left: -50vw;
  right: -50vw;
  height: 200vh;
  width: 200vw;
  z-index: 10002;
}

body:after {
  content: 'You can drop the file. :-)';
  font-size: 2em;
  text-align: center;
  line-height: 100vh;
  position: absolute;
  top: 10px;
  bottom: 10px;
  left: 10px;
  right: 10px;
  background-color: #eee;
  z-index: 10000;
  border-radius: 4px;
  border: thin solid #ccc;
  visibility: hidden;
  opacity: 0;
  transition: visibility 0s, opacity 0.5s ease;
}

body.dragged:after {
  opacity: 1;
  visibility: visible;
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>

<h3>Drag N Drop file upload without AJAX Demo</h3>
<p>Try drag and dropping a file. :-)</p>

<div class="form-group file-upload" required="required">
    <label class="cols-sm-2 control-label" for="document_file">File Upload</label><br>
    <div class="cols-sm-10">
      <div class="input-group">
        <span class="input-group-addon"><i class="fa fa-file" aria-hidden="true"></i></span>
        <div class="form-control" data-message="Click to select file or drag n drop it here">
          <input required="required" title="Click to select file or drag n drop it here" type="file" name="document[file]" id="document_file">
        </div>
      </div>
    </div>
  </div>

#3


-1  

It can be done by turning autoUpload to false, collecting the files in an array, then on form submit do a single ajax call with all the files together with the form data, as described here.

可以通过将autoUpload设置为false,收集数组中的文件,然后在表单提交上执行单个ajax调用,将所有文件与表单数据一起执行,如此处所述。