I'm looking for a component that can get a list of files from the user. It just needs to get the files, not upload. The uploading process is already implemented, it just needs a list of files. The component needs to fulfill the following requirements:
webkitRelativePath
for all filesThe closest I've gotten to achieving this is with Antd's Upload
component. The limitation here was that the only way to capture the file list is with its onChange
callback, which gets called once for every selected file. This means if a user is selecting thousands of files, which is a regular circumstance in my case, it will update the file list state thousands of times, causing thousands of rerenders and ultimately crashing the site.
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>
I tried a couple other libraries, but the second closest I've gotten was with the @rpldy/uploady library. I wrapped Antd's Dragger
component to utilize its visual aspects with rpldy's Uploady
and UploadDropZone
components for the functional aspects. The Dropzone component fulfills the first three criteria, however it does not return the webkitRelativePath
of the files in the file list.
<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>
Unfortunately, this is due to browser implementation decisions.
I re-created a simple version of your solution in this code sandbox.
In Firefox, I get the path as expected while in Chrome, the path is empty as you reported.
The MDN definition for the webkitRelativePath property is: "the file's path relative to the directory selected by the user in an element with its webkitdirectory attribute set. "
So it means it should only be available when actually using the file-selection dialog when the webkitdirectory
However, you're in luck, as I've faced this before and built in the capability to retain the path in the file name. You can activate this by passing: withFullPath to the html-dir-content package used by Rpldy's upload-drop-zone:
htmlDirContentParams={{ recursive: true, withFullPath: true }}
The resulting file data will look like this: