Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ React.render(<Upload />, container);
|component | "div"|"span" | "span"| wrap component name |
|action| string &#124; function(file): string &#124; Promise&lt;string&gt; | | form action url |
|method | string | post | request method |
|directory| boolean | false | support upload whole directory |
|directory| boolean \| 'nonClick' | false | support upload whole directory, 'nonClick' means unSupport click to upload directory |
|data| object/function(file) | | other data object to post or a function which returns a data object(a promise object which resolve a data object) |
|headers| object | {} | http headers to post, available in modern browsers |
|accept | string | | input accept attribute |
Expand Down
9 changes: 9 additions & 0 deletions docs/demo/dragOnlyDirectory.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
title: dragOnlyDirectory
nav:
title: Demo
path: /demo
---

<code src="../examples/dragOnlyDirectory.tsx"></code>

44 changes: 44 additions & 0 deletions docs/examples/dragOnlyDirectory.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/* eslint no-console:0 */
import React from 'react';
import Upload from 'rc-upload';

const props = {
action: '/upload.do',
type: 'drag',
directory: 'nonClick' as 'nonClick',
beforeUpload(file, fileList) {
console.log('beforeUpload', file.name, fileList);
},
onStart: file => {
console.log('onStart', file.name);
},
onSuccess(file) {
console.log('onSuccess', file);
},
onProgress(step, file) {
console.log('onProgress', Math.round(step.percent), file.name);
},
onError(err) {
console.log('onError', err);
},
style: { display: 'inline-block', width: 200, height: 200, background: '#eee' },
// openFileDialogOnClick: false
};

const Test = () => {
return (
<div
style={{
margin: 100,
}}
>
<div>
<Upload {...props}>
<a>开始上传,只支持拖拽上传文件夹</a>
</Upload>
</div>
</div>
);
};

export default Test;
2 changes: 1 addition & 1 deletion src/AjaxUploader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ class AjaxUploader extends Component<UploadProps> {
});
// because input don't have directory/webkitdirectory type declaration
const dirProps: any =
directory || folder ? { directory: 'directory', webkitdirectory: 'webkitdirectory' } : {};
directory === true || folder ? { directory: 'directory', webkitdirectory: 'webkitdirectory' } : {};

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

你好,此处的修改正确处理了 directory'nonClick' 时的情况,避免了点击时触发目录选择。

然而,我注意到组件中的其他地方未能正确地同时处理 directoryfolder 这两个 prop,这会导致使用 folder prop 时出现 bug。既然你正在修改相关的逻辑,建议在此 PR 中一并修复:

  1. onDataTransferFiles 方法 (L70, L79):
    此方法用于处理拖拽上传。目前它只解构并检查了 directory prop,忽略了 folder。这导致当使用 folder={true} 时,拖拽上传文件夹的功能会失效。

    • 应修改为:const { multiple, accept, directory, folder } = this.props;
    • 以及 if (directory || folder)
  2. onChange 方法 (L34, L37):
    此方法在通过文件对话框选择文件/文件夹后触发。它同样只检查了 directory prop 来决定是否要对文件进行 accept 过滤。当使用 folder={true} 时,文件过滤将不会按预期工作。

    • 应修改为:const { accept, directory, folder } = this.props;
    • 以及 (file: RcFile) => !(directory || folder) || attrAccept(file, accept)

修复这些问题可以确保在使用推荐的 folder prop 时,组件功能正常。

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

directory 已经标注废弃,改用 folder,所以你两个都得判断

Copy link
Author

@linhf123 linhf123 Sep 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

folder 不会走以下逻辑(在拖拽、复制时对文件夹内容进行读取并拍平),原 PR 看着只是开启原生文件夹选择
#643

onDataTransferFiles = async (dataTransfer: DataTransfer, existFileCallback?: () => void) => {
const { multiple, accept, directory } = this.props;
const items: DataTransferItem[] = [...(dataTransfer.items || [])];
let files: File[] = [...(dataTransfer.files || [])];
if (files.length > 0 || items.some(item => item.kind === 'file')) {
existFileCallback?.();
}
if (directory) {
files = await traverseFileTree(Array.prototype.slice.call(items), (_file: RcFile) =>
attrAccept(_file, this.props.accept),
);
this.uploadFiles(files);
} else {
let acceptFiles = [...files].filter((file: RcFile) => attrAccept(file, accept));
if (multiple === false) {
acceptFiles = files.slice(0, 1);
}
this.uploadFiles(acceptFiles);
}
};

拖拽场景下只能获取到文件夹对象(需要用户手动处理),不能获取内部文件
image

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

拖拽场景下只能获取到文件夹对象(需要用户手动处理),不能获取内部文件

手动处理指的是怎么处理?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

需要用户自己实现 onDataTransferFiles 中的实现,才可以获取文件夹下的文件

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

所以 folder 预期拖拽也需要直接返回文件夹中的子文件吗?

const events = disabled
? {}
: {
Expand Down
2 changes: 1 addition & 1 deletion src/interface.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export interface UploadProps
action?: Action;
method?: UploadRequestMethod;
/** @deprecated Please use `folder` instead */
directory?: boolean;
directory?: boolean | 'nonClick';

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

你好,将 'nonClick' 添加到 directory 类型是正确的。

不过,directory prop 已经被标记为 @deprecated,并建议使用 folder。为了保持一致性和避免未来混淆,是否考虑也将 'nonClick' 选项添加到 folder prop 中?

例如:

folder?: boolean | 'nonClick';

这样,用户就可以通过推荐的 folder prop 来使用这个新功能,例如 folder="nonClick"。这会让 API 更加清晰。

folder?: boolean;
data?: Record<string, unknown> | ((file: RcFile | string | Blob) => Record<string, unknown>);
headers?: UploadRequestHeader;
Expand Down