将拖放文件拖放到标准html文件输入中


163

如今,我们可以将文件拖放到一个特殊的容器中,并使用XHR 2上载它们。带有实时进度条等。非常酷的东西。这里的例子。

但是有时候我们不想那么酷。我想要的是将文件(一次很多)拖放到标准HTML文件输入中<input type=file multiple>

那可能吗?有什么方法可以从文件拖放中用正确的文件名(?)“填充”文件输入吗?(出于文件系统安全性原因,完整的文件路径不可用。)

为什么?因为我想提交一份普通表格。适用于所有浏览器和所有设备。拖放只是逐步增强,可以增强和简化UX。具有标准文件输入(+ multiple属性)的标准表单将在那里。我想添加HTML5增强功能。

编辑
我知道在某些浏览器中,您有时可以(几乎总是)将文件拖放到文件输入本身中。我知道Chrome通常会执行此操作,但是有时会失败,然后将文件加载到当前页面中(如果您要填写表单,则会导致很大的失败)。我想愚弄和浏览器证明。


1
如果要在兼容性中包括mac / safari,请准备一些痛苦的方法。
Shark8

1
@ Shark8实际上Safari / Mac是已经支持此功能的少数浏览器之一。
里卡多·托马西

实际上,没有任何浏览器支持此功能。文件输入字段是只读的(出于安全性考虑),这就是问题所在。愚蠢的安全!
鲁迪2011年

2
通过我的意思“拖放文件-许多在同一时间-到一个标准的HTML文件输入”。
里卡多·托马西

3
拖放多个文件以input type="file" multiple在Safari中正常工作
Lloyd

Answers:


71

以下内容适用于Chrome和FF,但我还没有找到涵盖IE10 +的解决方案:

// dragover and dragenter events need to have 'preventDefault' called
// in order for the 'drop' event to register. 
// See: https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Drag_operations#droptargets
dropContainer.ondragover = dropContainer.ondragenter = function(evt) {
  evt.preventDefault();
};

dropContainer.ondrop = function(evt) {
  // pretty simple -- but not for IE :(
  fileInput.files = evt.dataTransfer.files;

  // If you want to use some of the dropped files
  const dT = new DataTransfer();
  dT.items.add(evt.dataTransfer.files[0]);
  dT.items.add(evt.dataTransfer.files[3]);
  fileInput.files = dT.files;

  evt.preventDefault();
};
<!DOCTYPE html>
<html>
<body>
<div id="dropContainer" style="border:1px solid black;height:100px;">
   Drop Here
</div>
  Should update here:
  <input type="file" id="fileInput" />
</body>
</html>

您可能会想使用addEventListener或jQuery(等等)来注册您的evt处理程序-这只是为了简便起见。


3
哇!这样可行!?那正是我想要的。2年前没有工作。太棒了!当然,它在IE中不起作用=)重要的问题:是否存在可靠的功能检测?因此,您可以在IE中隐藏dropzone,因为它不起作用。
鲁迪'16

D'oh,那晚了一点:)现在,我只是在JS中使用简单的用户代理检查。当然,您必须测试MSIE Trident/(IE11)和Edge/(IE12)...
jlb

FF 48.0.2(Mac)在行上引发“ TypeError:设置仅具有吸气剂的属性” fileInput.files = evt.dataTransfer.files;。Safari和Chrome都可以正常工作。
里萨迪尼亚

2
这个例子不适用于Linux上的firefox 45,但是对我来说适用于chrome。我没有任何控制台错误,它只是没有显示任何文件被删除。
布莱恩·奥克利

1
实际上,我发布了一篇文章以尝试找到解决方案,但我自己想了办法。非常简单的更改,只是fileInputs [index] = ...将文件数据传递到特定输入,然后调用函数showNext添加新的输入stackoverflow.com/a/43397640/6392779
nick

51

我为此提出了解决方案。

此方法的拖放功能仅适用于Chrome,Firefox和Safari。(不知道它是否适用于IE10),但对于其他浏览器,“或单击此处”按钮可以正常工作。

将文件拖到某个区域上时,输入字段仅跟随您的鼠标,并且我还添加了一个按钮。

取消注释不透明度:0;文件输入仅可见,因此您可以查看正在发生的情况。


这就是为什么我还添加了一个按钮^^但是,是的,你是对的。我不会用它...否则我会!?
BjarkeCK

我希望我知道应该如何工作...似乎所有拖放功能都必须处理添加悬停效果...但是我真的不知道。在小提琴中看起来不错,但我认为我不能使用它,因为我需要支持Internet Explorer
nmz787 2015年

1
@PiotrKowalski我认为这有可能触发递归调用,直到调用堆栈溢出为止
John

2
我最终只使用了样式。使输入的宽度和高度为100%比移动它更好。
艾迪(Eddie)

2
有没有办法摆脱“无文件选择”的问题,它会一直与鼠标指针一起徘徊?@BjarkeCK
Abhishek Singh

27

这是HTML5的“ DTHML”方式。范式输入(Ricardo Tomasi指出,该格式为只读)。然后,如果将文件拖入,它将附加到表单。这将需要修改操作页面以接受以这种方式上传的文件。

function readfiles(files) {
  for (var i = 0; i < files.length; i++) {
    document.getElementById('fileDragName').value = files[i].name
    document.getElementById('fileDragSize').value = files[i].size
    document.getElementById('fileDragType').value = files[i].type
    reader = new FileReader();
    reader.onload = function(event) {
      document.getElementById('fileDragData').value = event.target.result;}
    reader.readAsDataURL(files[i]);
  }
}
var holder = document.getElementById('holder');
holder.ondragover = function () { this.className = 'hover'; return false; };
holder.ondragend = function () { this.className = ''; return false; };
holder.ondrop = function (e) {
  this.className = '';
  e.preventDefault();
  readfiles(e.dataTransfer.files);
}
#holder.hover { border: 10px dashed #0c0 !important; }
<form method="post" action="http://example.com/">
  <input type="file"><input id="fileDragName"><input id="fileDragSize"><input id="fileDragType"><input id="fileDragData">
  <div id="holder" style="width:200px; height:200px; border: 10px dashed #ccc"></div>
</form>

如果您可以将整个窗口设为拖放区域,那就更麻烦了,请参阅如何像Gmail一样检测到HTML5拖动事件进入和离开窗口的事件?


1
好的解决方案,但它在IE <10上不起作用,因为IE 9及以下版本不支持HTML5文件API :(
Develoger 2013年

1
这行代码:document.getElementById('fileDragData')。value = files [i] .slice(); 不需要,因为它已被reader.onload函数取代
kurdtpage

这是另一个不涉及文件上传的可爱的拖放应用程序。链接以防万一有人想学习更多。codepen.io/anon/pen/MOPvZK?editors=1010
William Entriken '17

1
IE 10解决方案将降级并且仅显示input type=file
William Entriken '17

我是否缺少某些内容,还是.value每次迭代前循环时都不断用最新文件覆盖属性?
凯文·伯克

13

//----------App.js---------------------//
$(document).ready(function() {
    var holder = document.getElementById('holder');
    holder.ondragover = function () { this.className = 'hover'; return false; };
    holder.ondrop = function (e) {
      this.className = 'hidden';
      e.preventDefault();
      var file = e.dataTransfer.files[0];
      var reader = new FileReader();
      reader.onload = function (event) {
          document.getElementById('image_droped').className='visible'
          $('#image_droped').attr('src', event.target.result);
      }
      reader.readAsDataURL(file);
    };
});
.holder_default {
    width:500px; 
    height:150px; 
    border: 3px dashed #ccc;
}

#holder.hover { 
    width:400px; 
    height:150px; 
    border: 3px dashed #0c0 !important; 
}

.hidden {
    visibility: hidden;
}

.visible {
    visibility: visible;
}
<!DOCTYPE html>

<html>
    <head>
        <title> HTML 5 </title>
        <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.js"></script>
    </head>
    <body>
      <form method="post" action="http://example.com/">
        <div id="holder" style="" id="holder" class="holder_default">
          <img src="" id="image_droped" width="200" style="border: 3px dashed #7A97FC;" class=" hidden"/>
        </div>
      </form>
    </body>
</html>


2
它向用户显示什么?您可以做一个小提琴或在线示例吗?
鲁迪

@Rudie,请单击运行代码片段并拖放一张图像进行查看,它将显示所放图像的预览。
迪帕克

6

从理论上讲,您可以在上添加一个元素<input/>,然后使用它的drop事件来捕获文件(使用File API)并将其传递给输入files数组。

除了文件输入是只读的。这是一个老问题。

但是,您可以完全绕过表单控件并通过XHR上传(不确定是否支持该控件):

您还可以在周围区域使用元素来取消Chrome中的放置事件,并防止加载文件的默认行为。

在输入和内容中删除多个文件已经可以在Safari和Firefox中使用。


6
就像我在问题中说的:我知道XHR2,并且我不想使用它。我猜最重要的部分是:“文件输入是只读的”。糟透了...取消放置事件并不是一个坏主意!不如我希望的那样好,但可能是最好的。拖放多个文件在Chrome中也可以。Chrome现在还允许上传目录。所有人都非常纠结并且没有帮助我的案件=(
Rudie 2011年

5

这就是我的想法。

使用Jquery和HTML。这会将其添加到插入文件中。

var dropzone = $('#dropzone')


dropzone.on('drag dragstart dragend dragover dragenter dragleave drop', function(e) {
    e.preventDefault();
    e.stopPropagation();
  })

dropzone.on('dragover dragenter', function() {
    $(this).addClass('is-dragover');
  })
dropzone.on('dragleave dragend drop', function() {
    $(this).removeClass('is-dragover');
  })  
  
dropzone.on('drop',function(e) {
	var files = e.originalEvent.dataTransfer.files;
	// Now select your file upload field 
	// $('input_field_file').prop('files',files)
  });
input {	margin: 15px 10px !important;}

.dropzone {
	padding: 50px;
	border: 2px dashed #060;
}

.dropzone.is-dragover {
  background-color: #e6ecef;
}

.dragover {
	bg-color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<div class="" draggable='true' style='padding: 20px'>
	<div id='dropzone' class='dropzone'>
		Drop Your File Here
	</div>
	</div>


4

对于仅CSS解决方案:

<div class="file-area">
    <input type="file">
    <div class="file-dummy">
        <span class="default">Click to select a file, or drag it here</span>
        <span class="success">Great, your file is selected</span>
    </div>
</div>

.file-area {
    width: 100%;
    position: relative;
    font-size: 18px;
}
.file-area input[type=file] {
    position: absolute;
    width: 100%;
    height: 100%;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    opacity: 0;
    cursor: pointer;
}
.file-area .file-dummy {
    width: 100%;
    padding: 50px 30px;
    border: 2px dashed #ccc;
    background-color: #fff;
    text-align: center;
    transition: background 0.3s ease-in-out;
}
.file-area .file-dummy .success {
    display: none;
}
.file-area:hover .file-dummy {
    border: 2px dashed #1abc9c;
}
.file-area input[type=file]:valid + .file-dummy {
    border-color: #1abc9c;
}
.file-area input[type=file]:valid + .file-dummy .success {
    display: inline-block;
}
.file-area input[type=file]:valid + .file-dummy .default {
    display: none;
}

https://codepen.io/Scribblerockerz/pen/qdWzJw修改


4

我知道Chrome有一些技巧:

当拖放文件到拖放区,你得到一个dataTransfer.files对象,这是一个FileList类型的对象,它包含了所有你拖动的文件。同时,<input type="file" />element具有property files,即同一FileList类型的对象。

因此,您可以简单地将dataTransfer.files对象分配给input.files属性。


3
是的,这些天确实如此。不是把戏。非常有意。也很故意很受限制。您不能将文件添加到列表中,也不能完全更改列表。拖放可以记住文件,然后添加到文件中,但input.files不能=(
Rudie

3

对于希望在2018年做到这一点的任何人,我都拥有一个比这里发布的所有旧东西更好,更简单的解决方案。您可以仅使用普通HTML,JavaScript和CSS制作一个漂亮的拖放框。

(目前仅适用于Chrome)

让我们从HTML开始。

<div>
<input type="file" name="file" id="file" class="file">
<span id="value"></span>
</div>

然后我们将介绍样式。

    .file {
        width: 400px;
        height: 50px;
        background: #171717;
        padding: 4px;
        border: 1px dashed #333;
        position: relative;
        cursor: pointer;
    }

    .file::before {
        content: '';
        position: absolute;
        background: #171717;
        font-size: 20px;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        width: 100%;
        height: 100%;
    }

    .file::after {
        content: 'Drag & Drop';
        position: absolute;
        color: #808080;
        font-size: 20px;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
    }

完成此操作后,它看起来已经不错了。但我想您会想知道您实际上传了什么文件,因此我们将使用一些JavaScript。还记得pfp值跨度吗?那就是我们打印出文件名的地方。

let file = document.getElementById('file');
file.addEventListener('change', function() {
    if(file && file.value) {
        let val = file.files[0].name;
        document.getElementById('value').innerHTML = "Selected" + val;
    }
});

就是这样。


我收到Uncaught TypeError:当我在Chrome下使用此代码时,无法读取null的属性'addEventListener'-在Chrome的最新版本中不起作用?
用火扑灭火焰

在最新版本的Chrome中,它对我来说效果很好。确保您使用正确的ID
Michael

1

@BjarkeCK的出色工作。我对其工作进行了一些修改,以将其用作jquery中的方法:

$.fn.dropZone = function() {
  var buttonId = "clickHere";
  var mouseOverClass = "mouse-over";

  var dropZone = this[0];
  var $dropZone = $(dropZone);
  var ooleft = $dropZone.offset().left;
  var ooright = $dropZone.outerWidth() + ooleft;
  var ootop = $dropZone.offset().top;
  var oobottom = $dropZone.outerHeight() + ootop;
  var inputFile = $dropZone.find("input[type='file']");
  dropZone.addEventListener("dragleave", function() {
    this.classList.remove(mouseOverClass);
  });
  dropZone.addEventListener("dragover", function(e) {
    console.dir(e);
    e.preventDefault();
    e.stopPropagation();
    this.classList.add(mouseOverClass);
    var x = e.pageX;
    var y = e.pageY;

    if (!(x < ooleft || x > ooright || y < ootop || y > oobottom)) {
      inputFile.offset({
        top: y - 15,
        left: x - 100
      });
    } else {
      inputFile.offset({
        top: -400,
        left: -400
      });
    }

  }, true);
  dropZone.addEventListener("drop", function(e) {
    this.classList.remove(mouseOverClass);
  }, true);
}

$('#drop-zone').dropZone();

工作小提琴


1
仅供参考:Fiddle链接已损坏。
jimiayler '18

1

几年后,我建立了这个库来将文件拖放到任何HTML元素中。

你可以像这样使用它

const Droppable = require('droppable');

const droppable = new Droppable({
    element: document.querySelector('#my-droppable-element')
})

droppable.onFilesDropped((files) => {
    console.log('Files were dropped:', files);
});

// Clean up when you're done!
droppable.destroy();

提交表单后,如何获取所选文件?
Nikhil VJ

-1

您可以做的是显示一个文件输入,并在透明的放置区域上覆盖它,请小心使用诸如的名称file[1]。{请确保enctype="multipart/form-data"在FORM标签内。}

然后,通过为文件2..number_of_files动态创建更多文件输入,使放置区域处理多余的文件,请确保使用相同的基本名称,并适当地填充值属性。

最后(前端)提交表单。


处理此方法所需要做的就是更改您的过程以处理文件数组。


1
最近,文件输入具有multiple属性。无需输入超过1个文件。那不是问题。如何将File对象放入文件输入中?我在想这需要一些代码示例...
Rudie 2011年

1
@Rudie你不能,那是问题。
里卡多·托马西

1
不能吗 多?是的你可以。我只是这么说。倍数不是问题。将文件从(拖放的)File对象获取到文件输入中就是问题所在。
鲁迪

@Rudie可以将Chrome / FF(使用该files属性)将文件拖到文件输入中,但是我没有在IE中管理-您有运气吗?
jlb

1
@jlb“使用文件属性”是什么意思?您能用相关代码回答吗?我正在寻找的内容在任何浏览器中均不起作用/不存在。
鲁迪
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.