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

Posted

技术标签:

【中文标题】没有AJAX的拖放文件上传,在前台同步?【英文标题】:Drag-and-drop file uploading without AJAX, synchronously in the foreground? 【发布时间】:2012-08-20 17:41:52 【问题描述】:

我有一个定期上传<input type="file"> 文件的网站,在提交表单时将数据发布到后端。

我想逐步增强表单,以便您可以将文件从浏览器外部拖放到视口中的任何位置(不仅仅是文件输入字段,如某些浏览器内置)以上传。

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

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

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

【问题讨论】:

在 Chrome 中,至少,你可以设置一个文件输入的文件放在 drop 上:jsfiddle.net/qMmPr。这就是你所追求的吗? @pimvdb 太漂亮了!正是我想要的。而且我忘了提到,由于这是针对内部服务的,因此仅 Chrome 就可以了。如果您将其写为答案(除了评论),我会将其标记为已接受。 【参考方案1】:

虽然不是真正的“同步”(javascript 执行实际上不会停止),但您可以通过编程方式设置<input type="file"> 选择的文件。事实上,这些元素和拖动共享它们的文件后端实现(FileFileList 实例),所以它真的很简单。更重要的是,由于两个前端都使用FileLists,拖动多个文件同样无缝。

这适用于 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
);

【讨论】:

@Henrik N:我现在意识到它不喜欢 Windows 上的 .lnk 文件。通过输入元素选择它们会选择快捷方式链接到的文件,而删除.lnk 文件会得到.lnk 文件本身。 对我的用例来说不是问题。再次感谢! 对跨浏览器解决方案有任何想法吗? (刚刚在 FF16 上测试了 prop():不工作) @Bogdan D:在input[type='file'] 元素上放置文件时,Firefox 似乎默认接受此行为(即不要插入上述 JavaScript 代码)。不过,不确定如何从整个文档中路由此行为。 @pimvdb 感谢您的回答(赞成),这种行为对我来说已经足够好了!现在进入 IE10 的奇特世界(当然以上都不起作用),我会发布任何有用的发现...【参考方案2】:

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

既然在<input type="file" /> 上拖放可以正常工作,为什么不在dragstart 上将其设为全屏以确保用户不会错过呢?反正他是在拖拖拉拉,所以此刻他的意图很明确。

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

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

这是sn-p:

  $('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】:

可以通过将 autoUpload 设置为 false,将文件收集到一个数组中,然后在提交表单时对所有文件和表单数据进行一次 ajax 调用,如 here 所述。

【讨论】:

谢谢你,但请注意问题中的“没有 Ajax”:) 没错,我的方法仍然使用 ajax,但它只是一个请求,并且以这样的方式显示为用户的正常提交操作。 是的,在很多情况下这绝对是一个很好的解决方案。但是正如问题所提到的,通过 Ajax 进行操作意味着您需要特殊的错误处理等等。在这种情况下,这不是我所追求的,但我仍然很欣赏输入:)

以上是关于没有AJAX的拖放文件上传,在前台同步?的主要内容,如果未能解决你的问题,请参考以下文章

在 Asp.net 中使用 HTML5 的拖放上传文件

用于 Internet Explorer 的拖放文件上传库

上传文件时检测到取消的拖放操作

JQuery - 拖放文件 - 如何获取文件信息?

一个轻量级且非常可配置的jQuery插件,用于使用ajax(同步)上传文件;包括对队列、进度跟踪和拖放的支持。

有没有好的 jQuery 拖放文件上传插件?