【发布时间】:2023-01-10 17:39:29
【问题描述】:
我正在寻找一个可以从用户那里获取文件列表的组件。它只需要获取文件,而不是上传。上传过程已经实现,它只需要一个文件列表。该组件需要满足以下要求:
- 限于只要目录选择
- 支持通过文件对话框拖放到选择之上
- 在一次回调中捕获所有选定的文件,这意味着仅一次状态更新
- 文件列表也应该在上传前可以访问,以便可以在预览中使用
- 返回所有文件的
webkitRelativePath
我最接近实现这一点的是Antd'sUpload组件。这里的限制是捕获文件列表的唯一方法是使用它的onChange回调,它被调用一次每一个选定的文件。这意味着如果用户选择数千个文件,这在我的案例中是常见的情况,它将更新文件列表状态数千次,导致数千次重新呈现并最终导致网站崩溃。
const uploadProps = {
accept: '*',
multiple: true,
customRequest: () => {},
onRemove: (file: UploadFile) => {
const index = props.fileList.indexOf(file)
const newFileList = [...props.fileList]
newFileList.splice(index, 1)
props.setFileList(newFileList)
},
beforeUpload: () => {
return false
},
onChange: (info: UploadChangeParam<UploadFile<any>>) => {
if (JSON.stringify(info.fileList) !== JSON.stringify(props.fileList)) {
console.log(info.fileList)
props.setFileList(info.fileList)
}
if (info.fileList.length === 0 && props.progress !== 0) props.setProgress(0)
},
directory: true
}
<Dragger
{...uploadProps}
fileList={props.fileList.slice(fileListIndex, fileListIndex + 10)}
>
<p className='ant-upload-text'>
<b>Uploading to:</b> {S3_BUCKET.split('/').slice(1).join('/')}
</p>
<br></br>
<p className='ant-upload-drag-icon'>
<InboxOutlined />
</p>
<p className='ant-upload-text'>
Browse or drag folder to upload
<br />
<strong>Uploading {props.fileList.length} files</strong>
<br />
Showing files {props.fileList.length ? fileListIndex + 1 : 0}-
{Math.min(fileListIndex + 10, props.fileList.length)}
</p>
</Dragger>
我尝试了其他几个库,但我得到的第二个最接近的库是 @rpldy/uploady 库。我包装了 Antd 的 Dragger 组件以利用其视觉方面与 rpldy 的 Uploady 和 UploadDropZone 组件的功能方面。 Dropzone 组件满足前三个条件,但它不返回文件列表中文件的 webkitRelativePath。
<Uploady autoUpload={false} accept={'*'} webkitdirectory>
<UploadDropZone
onDragOverClassName='drag-over'
htmlDirContentParams={{ recursive: true }}
dropHandler={async (e, getFiles) => {
let fileList = await getFiles()
props.setFileList(fileList)
fileList.map((file) => console.log(file.webkitRelativePath)) // Empty log
return fileList
}}
>
<Dragger
openFileDialogOnClick={false}
customRequest={() => {}}
onRemove={(file: UploadFile) => {
const index = props.fileList.indexOf(file as unknown as File)
const newFileList = [...props.fileList]
newFileList.splice(index, 1)
props.setFileList(newFileList)
}}
fileList={
props.fileList.slice(
fileListIndex,
fileListIndex + 10
) as unknown as UploadFile[]
}
>
<p className='ant-upload-text'>
<b>Uploading to:</b> {S3_BUCKET.split('/').slice(1).join('/')}
</p>
<br></br>
<p className='ant-upload-drag-icon'>
<InboxOutlined />
</p>
<p className='ant-upload-text'>
<>
Browse or drag folder to upload
<br />
<UploadButton text='Browse' />
<br />
<strong>Uploading {props.fileList.length} files</strong>
<br />
Showing files{' '}
{props.fileList.length
? fileListIndex + 1 > props.fileList.length
? setFileListIndex(fileListIndex - 10)
: fileListIndex + 1
: 0}
-{Math.min(fileListIndex + 10, props.fileList.length)}
</>
</p>
</Dragger>
</UploadDropZone>
</Uploady>
【问题讨论】:
标签: reactjs typescript file upload