交互

点击上传多个文件 multiple

用户可以选择多个文件,然后文件列表会显示出来。需要注意的是,文件的选择是通过<input type="file" multiple>标签来实现的,其中multiple属性允许选择多个文件。

点击上传多个文件
1const { useState, useEffect, ChangeEvent } = React
2
3export default () => {
4  const [files, setFiles] = useState([])
5  const [fileList, setFileList] = useState([])
6
7  const handleChange = (e) => {
8    const files = e.target.files
9    if (files)
10      setFiles(Array.from(files))
11  }
12
13  useEffect(() => {
14    const fileList = files.map(file => file.name)
15    setFileList(fileList)
16  }, [files])
17
18  return (
19    <div>
20      <input type="file" multiple onChange={handleChange} />
21      <ul>
22        {fileList.map((file, index) => (
23          <li key={index}>{file}</li>
24        ))}
25      </ul>
26    </div>
27  )
28}

点击上传文件夹 odirectory

允许用户上传整个文件夹,而不仅仅是单个文件。使用<input type="file" webkitdirectory mozdirectory odirectory>标签来启用文件夹上传功能。在处理时,代码会检查文件是否为文件夹,然后递归读取文件夹中的文件列表。

webkitdirectorymozdirectoryodirectory是用于HTML文件输入元素<input type="file">的属性,用于启用文件夹上传功能。它们是浏览器厂商引入的一些非标准属性,因此在不同浏览器中的支持程度可能会有所不同。

webkitdirectory

支持浏览器: WebKit内核的浏览器,如Google ChromeSafari。作用: 当设置webkitdirectory属性时,文件输入框会打开一个文件选择对话框,允许用户选择文件夹而不仅仅是单个文件。这允许用户选择整个文件夹中的所有文件,而不必一个一个地选择。

mozdirectory

支持浏览器: Mozilla Firefox浏览器。作用: 类似于webkitdirectorymozdirectory属性用于启用文件夹上传功能。它允许用户在Firefox浏览器中选择整个文件夹进行上传。

odirectory

支持浏览器: 这是Opera浏览器引入的一个属性。作用: 类似于前两者,odirectory属性用于启用文件夹上传功能。用户可以在 Opera 浏览器中选择整个文件夹。需要注意的是,由于这些属性不是 HTML 标准的一部分,因此在不同浏览器中的支持可能不稳定。它们主要用于特定浏览器内核的实现,因此在跨浏览器应用程序中使用时,需要小心检查并处理不同浏览器的兼容性。在现代 Web 开发中,通常使用其他更标准的方法,如使用JavaScript来处理文件上传和文件夹选择,以确保跨浏览器兼容性。

  • 兼容性
  • ::: react-demo 点击上传文件夹
1const { useState, useEffect, ChangeEvent } = React
2export default () => {
3  const [files, setFiles] = useState([])
4  const [fileList, setFileList] = useState([])
5
6  const handleChange = (e) => {
7    const files = e.target.files
8    if (files)
9      setFiles(Array.from(files))
10  }
11
12  useEffect(() => {
13    const fileList = files.map(file => file.name)
14    setFileList(fileList)
15  }, [files])
16
17  return (
18    <div>
19      <input type="file" webkitdirectory mozdirectory odirectory onChange={handleChange} />
20      <ul>
21        {fileList.map((file, index) => (
22          <li key={index}>{file}</li>
23        ))}
24      </ul>
25    </div>
26  )
27}

拖拽上传文件和文件夹dataTransfer

::: react-demo 拖拽上传文件和文件夹

用户可以将文件或文件夹拖拽到指定区域进行上传。代码使用onDragOveronDrop事件来处理拖拽操作,并通过e.dataTransfer.items来获取拖拽的文件和文件夹列表。

1const { useState, useEffect, ChangeEvent } = React
2
3export default () => {
4  const [files, setFiles] = useState([])
5  const [fileList, setFileList] = useState([])
6
7  const handleChange = (e) => {
8    const files = e.target.files
9    if (files)
10      setFiles(Array.from(files))
11  }
12
13  const handleDragOver = (e) => {
14    e.preventDefault()
15  }
16
17  const handleDrop = (e) => {
18    e.preventDefault()
19    const files = e.dataTransfer.items
20    // 判断是否为文件夹
21    for (const item of files) {
22      const entry = item.webkitGetAsEntry()
23      if (entry.isDirectory) {
24        console.log('is directory')
25        entry.createReader().readEntries((entries) => {
26          console.log(entries)
27          const fileList = entries.map(entry => entry.name)
28          setFileList(fileList)
29        })
30      }
31      else {
32        console.log('is file')
33        entry.file((file) => {
34          console.log(file.name)
35          setFiles([file])
36        })
37      }
38    }
39  }
40
41  useEffect(() => {
42    const fileList = files.map(file => file.name)
43    setFileList(fileList)
44  }, [files])
45
46  return (
47    <div>
48      <input type="file" multiple onChange={handleChange} onDragOver={handleDragOver} onDrop={handleDrop} />
49      <ul>
50        {fileList.map((file, index) => (
51          <li key={index}>{file}</li>
52        ))}
53      </ul>
54    </div>
55  )
56}

如何实现多文件上传

  1. 把选择的文件放到一个数组里 files 一次性发送到服务器,服务器接收到后,再一次性处理
  2. 把选择的文件形成不同的请求,每个请求只处理一个文件一般会选择第二种,第一种方式,如果文件很多,其中一个文件上传失败,需要重新上传所有文件,很难保证对每一个文件的独立控制。第二种方式,需要控制并发数,如果并发数太大,可能会导致服务器崩溃,可以对于每个文件进行单独的控制,比如上传进度,上传取消等。

上传进度progress

使用XMLHttpRequest对象来发送文件,并通过xhr.upload.onprogress事件来监听上传进度。上传进度以百分比形式显示给用户。 ::: react-demo 上传进度

1const { useState } = React
2export default () => {
3  const [progress, setProgress] = useState(0)
4
5  const handleUpload = (e) => {
6    const file = e.target.files?.[0]
7    if (file) {
8      const xhr = new XMLHttpRequest()
9      xhr.open('POST', '/upload')
10      xhr.upload.onprogress = (event) => {
11        if (event.lengthComputable) {
12          const percentComplete = Math.round((event.loaded / event.total) * 100)
13          setProgress(percentComplete)
14        }
15      }
16      xhr.send(file)
17    }
18  }
19
20  return (
21    <div>
22      <input type="file" onChange={handleUpload} />
23      <p>
24        {progress}
25        %
26      </p>
27    </div>
28  )
29}

如何实现上传进度追踪

::: react-demo 上传进度追踪用户可以点击Cancel按钮来中止上传操作,这对于用户体验和控制非常有用。代码使用xhr.abort()方法来中止上传操作。

1const { useState } = React
2export default () => {
3  const [progress, setProgress] = useState(0)
4  const [xhr, setXhr] = useState(null)
5
6  const handleUpload = (e) => {
7    const file = e.target.files?.[0]
8    if (file) {
9      const xhr = new XMLHttpRequest()
10      xhr.open('POST', '/upload')
11      xhr.upload.onprogress = (event) => {
12        if (event.lengthComputable) {
13          const percentComplete = Math.round((event.loaded / event.total) * 100)
14          setProgress(percentComplete)
15        }
16      }
17      xhr.send(file)
18      setXhr(xhr)
19    }
20  }
21
22  const handleCancel = () => {
23    if (xhr) {
24      xhr.abort()
25      setXhr(null)
26      setProgress(0)
27    }
28  }
29
30  return (
31    <div>
32      <input type="file" onChange={handleUpload} />
33      <p>
34        {progress}
35        %
36      </p>
37      {xhr && <button onClick={handleCancel}>Cancel</button>}
38    </div>
39  )
40}