jjzjj

javascript - 如何以编程方式更改文件输入的 FileList?

coder 2023-08-05 原文

我有一个类型为 "file"input,我想更改它的 files 列表。示例:

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

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

最佳答案

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


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

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

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

所有主流浏览器都支持它,但 IE 不支持,Safari 14.1 之后才支持。你可以检查它 here

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

const dataTransfer = new DataTransfer()

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

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

每个 DataTransfer 都有一个与之关联的 DataTransferItemList(可通过其 items 属性访问),它具有向 DataTransfer 添加(和删除)项的方法.

要添加文件,您必须对其调用 add 方法并传递一个 File 对象。

像这样:

dataTransfer.items.add(file)

如果您的数据不在File 对象中(例如字节的BlobArrayBuffer),您可以使用File constructor ,将数据、文件名和可选的具有 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)
 |                                                            ^
 |                                                            |
<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
  5. 中的文件

另请参阅有关该主题的 this answer of mine

为此,我创建了一对函数,用于从 File 的数组中获取和设置文件列表,因此可以在数组上执行转换,而不用做复杂的事情数据传输:

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)
  input.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)
  input.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)
})
Hint: hover over the file input to see the list of all files in it <br>
<input type="file" id="fileInput" multiple ><br><br>
<button id="removeFirst">Remove first file</button><br>
<button id="removeLastModified">Remove latest modified file</button><br>
<button id="addFile">Append file from this input</button> <input type="file" id="addFileInput" /><br>
<button id="addRandomHello">Add a hello.txt file to a random place</button><br>

关于javascript - 如何以编程方式更改文件输入的 FileList?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5632629/

有关javascript - 如何以编程方式更改文件输入的 FileList?的更多相关文章

  1. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  2. ruby - 其他文件中的 Rake 任务 - 2

    我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时

  3. ruby-on-rails - 在 Rails 中将文件大小字符串转换为等效千字节 - 2

    我的目标是转换表单输入,例如“100兆字节”或“1GB”,并将其转换为我可以存储在数据库中的文件大小(以千字节为单位)。目前,我有这个:defquota_convert@regex=/([0-9]+)(.*)s/@sizes=%w{kilobytemegabytegigabyte}m=self.quota.match(@regex)if@sizes.include?m[2]eval("self.quota=#{m[1]}.#{m[2]}")endend这有效,但前提是输入是倍数(“gigabytes”,而不是“gigabyte”)并且由于使用了eval看起来疯狂不安全。所以,功能正常,

  4. ruby-on-rails - Ruby on Rails 迁移,将表更改为 MyISAM - 2

    如何正确创建Rails迁移,以便将表更改为MySQL中的MyISAM?目前是InnoDB。运行原始执行语句会更改表,但它不会更新db/schema.rb,因此当在测试环境中重新创建表时,它会返回到InnoDB并且我的全文搜索失败。我如何着手更改/添加迁移,以便将现有表修改为MyISAM并更新schema.rb,以便我的数据库和相应的测试数据库得到相应更新? 最佳答案 我没有找到执行此操作的好方法。您可以像有人建议的那样更改您的schema.rb,然后运行:rakedb:schema:load,但是,这将覆盖您的数据。我的做法是(假设

  5. ruby-on-rails - Rails 3 中的多个路由文件 - 2

    Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题

  6. ruby - 将差异补丁应用于字符串/文件 - 2

    对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl

  7. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  8. ruby - 如何以所有可能的方式将字符串拆分为长度最多为 3 的连续子字符串? - 2

    我试图获取一个长度在1到10之间的字符串,并输出将字符串分解为大小为1、2或3的连续子字符串的所有可能方式。例如:输入:123456将整数分割成单个字符,然后继续查找组合。该代码将返回以下所有数组。[1,2,3,4,5,6][12,3,4,5,6][1,23,4,5,6][1,2,34,5,6][1,2,3,45,6][1,2,3,4,56][12,34,5,6][12,3,45,6][12,3,4,56][1,23,45,6][1,2,34,56][1,23,4,56][12,34,56][123,4,5,6][1,234,5,6][1,2,345,6][1,2,3,456][123

  9. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  10. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

    使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

随机推荐