如何更改文件输入的文件列表

时间:2011-04-12 08:50:42

标签: javascript html set

我有input类型"file",我想更改其files列表。例如:

<input type = "file" id = "fileinput" />
<script type = "text/javascript>
  document.getElementById("fileinput").files = [10];
</script>

问题是未设置fileinput元素的files列表。我该怎么做?

3 个答案:

答案 0 :(得分:15)

出于安全原因,浏览器会阻止javascript更改将要上传的文件:只有用户可以通过用户界面选择文件。这是为了防止邪恶的脚本上传/etc/passwd,例如,在用户不知道的情况下。

一个例外是在表单上调用“reset”将清除文件或文件列表,但是您永远不能以编程方式添加。

答案 1 :(得分:0)

确实不可能本地文件添加到用户没有请求的文件输入中;但是,一种方法可以向文件输入中添加BlobFile对象删除特定文件 .

等待 10 年后,解决方案终于来了。


要更改文件输入中的文件,您必须将 fileInput.files 替换为另一个 FileList 对象。

问题在于 FileList 对象是不可变的并且没有构造函数暴露给 JS。

创建自定义 FileList 的唯一方法是滥用 DataTransfer,这是一种用于通过拖放或剪贴板传输文件的 API。

您可以通过调用不带参数的构造函数来创建 DataTransfer

const dataTransfer = new DataTransfer()

创建的 DataTransfer 对象有一个 files 属性,幸运的是,它是一个包含 FileList 拥有的所有文件的 DataTransfer

但是如何将文件添加到 DataTransfer 中?

DataTransfer 有一个与之关联的 DataTransferItemList(可通过其 items 属性访问),该 add 具有向 DataTransfer 添加(和删除)项目的方法。< /p>

要将文件添加为项目,您必须对其调用 File 方法并传递一个 File constructor 对象。

像这样:

dataTransfer.items.add(file)

如果您的数据不在 File 对象中(例如 BlobArrayBuffer 字节),您可以使用 this answer of mine,传递数据,文件名,以及可选的具有 typelastModified 属性的对象:

const file = new File([blob], 'file.txt', {type: 'text/plain', lastModified: modificationDate})

所以,总而言之,我们有这样的事情:

new DataTransfer()
 |
 v
DataTransfer 
 |  |
 |  +--[ .items ]---> DataTransferItemList
 |                      |
 +--[ .files ]-----+    +---[ .add(file) ]---> DataTransferItem
                   |                ^
                   v                |
 +--[ .files ]--- FileList          |
 |                                  +--- File <--- new File(data, filename, options)
 |                                                           ^
 v                                                           |
<input type="file">     Blob ----------+---------------------+
                        ArrayBuffer ---+
                        String --------+

我确定这现在很乱,但让我们看看一些代码!

如果您想创建一个名为 hello.txt 的包含 Hello world! 的文件并将输入设置为包含此文件,您可以这样做:

const fileInput = document.getElementById('fileInput')

const dataTransfer = new DataTransfer()

const file = new File(['Hello world!'], 'hello.txt', {type: 'text/plain'})

dataTransfer.items.add(file)

fileInput.files = dataTransfer.files
<p>Notice that the file input contains "hello.txt" now: </p>
<input type="file" id="fileInput" />

但不是替换文件,您如何编辑输入中已有的文件列表?

  1. 创建一个DataTransfer
  2. 从输入中添加所有现有文件,除了要删除的文件
  3. 添加您要添加​​的文件
  4. 将输入中的文件替换为 DataTransfer 中的文件

另见关于该主题的 {{3}}。

为此,我创建了一对函数,用于从 File 数组中获取和设置文件列表,因此可以在数组上执行转换,而不是使用 {{1} 执行复杂的操作}s:

DataTransfer

您可以像这样使用它们:

function getFiles(input){
  const files = new Array(input.files.length)
  for(let i = 0; i < input.files.length; i++)
    files[i] = input.files.item(i)
  return files
}

function setFiles(input, files){
  const dataTransfer = new DataTransfer()
  for(const file of files)
    dataTransfer.items.add(file)
  fileInput.files = dataTransfer.files
}
function getFiles(input){
  const files = new Array(input.files.length)
  for(let i = 0; i < input.files.length; i++)
    files[i] = input.files.item(i)
  return files
}

function setFiles(input, files){
  const dataTransfer = new DataTransfer()
  for(const file of files)
    dataTransfer.items.add(file)
  fileInput.files = dataTransfer.files
}

const fileInput = document.querySelector('#fileInput')

document.querySelector('#removeFirst').addEventListener('click', () => {
  const files = getFiles(fileInput)
  
  files.shift()
  
  setFiles(fileInput, files)
})
document.querySelector('#removeLastModified').addEventListener('click', () => {
  const files = getFiles(fileInput)
  
  let latest = 0, latestIndex
  for(let i = 0; i < files.length; i++)
    if(files[i].lastModified > latest){
      latest = files[i].lastModified
      latestIndex = i
    }
  files.splice(latestIndex, 1)  
  
  setFiles(fileInput, files)
})
document.querySelector('#addFile').addEventListener('click', () => {
  const files = getFiles(fileInput)
  
  const newFiles = getFiles(document.querySelector('#addFileInput'))
  files.push(...newFiles)
  
  setFiles(fileInput, files)
})
document.querySelector('#addRandomHello').addEventListener('click', () => {
  const files = getFiles(fileInput)
  
  const newFile = new File(['Hello world!'], 'hello.txt', {type: 'text/plain'})
  const index = Math.floor(Math.random() * (files.length + 1))
  files.splice(index, 0, newFile)
  
  setFiles(fileInput, files)
})

答案 2 :(得分:-1)

您想要使用multiple元素上的input属性。这样,在较新的浏览器中,用户将能够选择要上传的多个文件。