首页 > 解决方案 > 如何分别从用户那里获取多个文件?

问题描述

目前,我正在开发拖放功能。问题是我无法弄清楚如何分别从用户那里获取多个文件。假设我们有一个拖放区容器,当用户将图像拖放到该容器时,它会将它们分配给<input type="file">. 比方说,用户在这里放了一张图片,然后决定放另一张图片,我们必须以某种方式将第二张图片添加到输入中。我尝试在互联网上寻找解决方案(当然:))但没有找到解决这个问题的方法。

这是我的 HTML:

<form action="" method="POST" enctype="multipart/form-data">

    <div class="drop_zone">
        <span class="drop_zone__prompt">Drop your files here</span>
        <input required name="images" type="file" multiple class="drop_zone__input">
    </div>
    <input type="submit" value="Отправить">
</form>

JavaScript:

document.querySelectorAll('.drop_zone__input').forEach(InputElement => {

    const dropZoneElement = InputElement.closest('.drop_zone');

    dropZoneElement.addEventListener('drop', e => {
        e.preventDefault();

        if (e.dataTransfer.files.length){
            InputElement.files = e.dataTransfer.files;
        }
    });

我试过这个:

dropZoneElement.addEventListener('drop', e => {
    e.preventDefault();

    if (e.dataTransfer.files.length){
        myfiles = e.dataTransfer.add(InputElement.files);
        InputElement.files = myfiles;
    }
});

但它返回错误说'e.dataTransfer.add 不是函数'

为什么我尝试这个:

我在这里找到了 add() 方法

这篇文章说:

DataTransferItemList 对象是代表被拖动项目的 DataTransferItem 对象的列表。在拖动操作期间,每个 DragEvent 都有一个 dataTransfer 属性,该属性是一个 DataTransferItemList

标签: javascript

解决方案


实际上,有一种方法可以做到这一点。这远非简单,但它确实有效。

创建对象的唯一方法FileList是创建自定义DataTransfer对象。您可以使用 向其中添加文件,并通过dataTransfer.items.add()获取对应的文件。FileListdataTransfer.files

DataTransfer因此,每次要添加文件时都创建一个新对象,将现有文件和新文件添加到其中,并将其分配给输入元素FileListfiles属性。

注意:您不能DataTransfer为此使用 drop 事件的对象,因为它是只读的。

document.querySelectorAll('.drop_zone__input').forEach(InputElement => {
  const dropZoneElement = InputElement.closest('.drop_zone');
  dropZoneElement.addEventListener('dragover', e => {
    e.preventDefault()
  });
  dropZoneElement.addEventListener('drop', e => {
     e.preventDefault();

     //Create a new DataTransfer object
     const dataTransfer = new DataTransfer

     //Add new files from the event's DataTransfer
     for(let i = 0; i < e.dataTransfer.files.length; i++)
       dataTransfer.items.add(e.dataTransfer.files[i])

     //Add existing files from the input element
     for(let i = 0; i < InputElement.files.length; i++)
       dataTransfer.items.add(InputElement.files[i])

     //Assign the files to the input element
     InputElement.files = dataTransfer.files
  });
})
.drop_zone{
  height: 200px;
  width: 200px;
  border: solid black 1px;
}
<form action="" method="POST" enctype="multipart/form-data">

  <div class="drop_zone">
    <span class="drop_zone__prompt">Drop your files here</span>
    <input required name="images" type="file" multiple class="drop_zone__input">
  </div>
  <input type="submit" value="Отправить">
</form>

您还可以重复使用相同的DataTransfer对象,因此您不必重新添加现有文件。

但是,在这种情况下,您还必须处理input输入元素上的事件。

document.querySelectorAll('.drop_zone__input').forEach(InputElement => {
  const dropZoneElement = InputElement.closest('.drop_zone');
  
  const dataTransfer = new DataTransfer
  dropZoneElement.addEventListener('dragover', e => {
    e.preventDefault()
  });
  dropZoneElement.addEventListener('drop', e => {
     e.preventDefault();

     //Add new files from the event's DataTransfer
     for(let i = 0; i < e.dataTransfer.files.length; i++)
       dataTransfer.items.add(e.dataTransfer.files[i])
       

     //Assign the files to the input element
     InputElement.files = dataTransfer.files
  });
  
   InputElement.addEventListener('input', e => {
    dataTransfer.items.clear()
    for(let i = 0; i < InputElement.files.length; i++)
      dataTransfer.items.add(InputElement.files[i])
  })
})
.drop_zone{
  height: 200px;
  width: 200px;
  border: solid black 1px;
}
<form action="" method="POST" enctype="multipart/form-data">

  <div class="drop_zone">
    <span class="drop_zone__prompt">Drop your files here</span>
    <input required name="images" type="file" multiple class="drop_zone__input">
  </div>
  <input type="submit" value="Отправить">
</form>

或者,如果您想在与文件输入交互时添加文件而不是替换它们,您可以这样做:

document.querySelectorAll('.drop_zone__input').forEach(InputElement => {
  const dropZoneElement = InputElement.closest('.drop_zone');
  
  const dataTransfer = new DataTransfer
  dropZoneElement.addEventListener('dragover', e => {
    e.preventDefault()
  });
  dropZoneElement.addEventListener('drop', e => {
     e.preventDefault();

     //Add new files from the event's DataTransfer
     for(let i = 0; i < e.dataTransfer.files.length; i++)
       dataTransfer.items.add(e.dataTransfer.files[i])
       

     //Assign the files to the input element
     InputElement.files = dataTransfer.files
  });
  
   InputElement.addEventListener('input', e => {
    e.preventDefault()
    
    for(let i = 0; i < InputElement.files.length; i++)
      dataTransfer.items.add(InputElement.files[i])
    InputElement.files = dataTransfer.files
  })
})
.drop_zone{
  height: 200px;
  width: 200px;
  border: solid black 1px;
}
<form action="" method="POST" enctype="multipart/form-data">

  <div class="drop_zone">
    <span class="drop_zone__prompt">Drop your files here</span>
    <input required name="images" type="file" multiple class="drop_zone__input">
  </div>
  <input type="submit" value="Отправить">
</form>

DataTransfer您可以使用以下方法从对象中删除文件dataTransfer.items.remove()

document.querySelectorAll('.drop_zone__input').forEach(InputElement => {
  const dropZoneElement = InputElement.closest('.drop_zone');
  const removeFirstElement = dropZoneElement.querySelector('.drop_zone__remove_first')
  
  const dataTransfer = new DataTransfer
  dropZoneElement.addEventListener('dragover', e => {
    e.preventDefault()
  });
  dropZoneElement.addEventListener('drop', e => {
     e.preventDefault();

     //Add new files from the event's DataTransfer
     for(let i = 0; i < e.dataTransfer.files.length; i++)
       dataTransfer.items.add(e.dataTransfer.files[i])
       

     //Assign the files to the input element
     InputElement.files = dataTransfer.files
  });
  
   InputElement.addEventListener('input', e => {
    e.preventDefault()
    
    for(let i = 0; i < InputElement.files.length; i++)
      dataTransfer.items.add(InputElement.files[i])
    InputElement.files = dataTransfer.files
  })
  
  removeFirstElement.addEventListener('click', () => {
    dataTransfer.items.remove(0)
    InputElement.files = dataTransfer.files
  })
})
.drop_zone{
  height: 200px;
  width: 200px;
  border: solid black 1px;
}
<form action="" method="POST" enctype="multipart/form-data">

  <div class="drop_zone">
    <span class="drop_zone__prompt">Drop your files here</span>
    <input required name="images" type="file" multiple class="drop_zone__input">
    <input type="button" class="drop_zone__remove_first" value="Remove first file">
  </div>
  <input type="submit" value="Отправить">
</form>


推荐阅读