@opensumi/ide-addons 2.21.13 → 2.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/browser/chrome-devtools.contribution.js.map +1 -1
- package/lib/browser/connection-rtt-contribution.js.map +1 -1
- package/lib/browser/connection-rtt-service.js.map +1 -1
- package/lib/browser/file-content-update-time.contribution.js +4 -4
- package/lib/browser/file-content-update-time.contribution.js.map +1 -1
- package/lib/browser/file-drop.contribution.js.map +1 -1
- package/lib/browser/file-drop.service.js.map +1 -1
- package/lib/browser/file-search.contribution.d.ts.map +1 -1
- package/lib/browser/file-search.contribution.js +15 -6
- package/lib/browser/file-search.contribution.js.map +1 -1
- package/lib/browser/index.js.map +1 -1
- package/lib/browser/language-change.contribution.js +1 -1
- package/lib/browser/language-change.contribution.js.map +1 -1
- package/lib/browser/status-bar-contribution.js.map +1 -1
- package/lib/browser/toolbar-customize/toolbar-customize.contribution.js.map +1 -1
- package/lib/browser/toolbar-customize/toolbar-customize.js +1 -1
- package/lib/browser/toolbar-customize/toolbar-customize.js.map +1 -1
- package/lib/node/connection-rtt-service.js.map +1 -1
- package/lib/node/file-drop.service.js.map +1 -1
- package/lib/node/index.js.map +1 -1
- package/package.json +17 -16
- package/src/browser/chrome-devtools.contribution.ts +66 -0
- package/src/browser/connection-rtt-contribution.ts +82 -0
- package/src/browser/connection-rtt-service.ts +15 -0
- package/src/browser/file-content-update-time.contribution.ts +256 -0
- package/src/browser/file-drop.contribution.ts +17 -0
- package/src/browser/file-drop.service.ts +165 -0
- package/src/browser/file-search.contribution.ts +612 -0
- package/src/browser/index.ts +44 -0
- package/src/browser/language-change.contribution.ts +45 -0
- package/src/browser/status-bar-contribution.ts +28 -0
- package/src/browser/toolbar-customize/style.module.less +52 -0
- package/src/browser/toolbar-customize/toolbar-customize.contribution.ts +59 -0
- package/src/browser/toolbar-customize/toolbar-customize.tsx +147 -0
- package/src/common/index.ts +46 -0
- package/src/index.ts +1 -0
- package/src/node/connection-rtt-service.ts +10 -0
- package/src/node/file-drop.service.ts +34 -0
- package/src/node/index.ts +37 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { Injectable, Autowired } from '@opensumi/di';
|
|
2
|
+
import { ClientAppContribution, Domain } from '@opensumi/ide-core-browser';
|
|
3
|
+
import { PreferenceSchema, PreferenceSchemaProvider, PreferenceService } from '@opensumi/ide-core-browser';
|
|
4
|
+
import {
|
|
5
|
+
debounce,
|
|
6
|
+
IReporterService,
|
|
7
|
+
StaleLRUMap,
|
|
8
|
+
OnEvent,
|
|
9
|
+
URI,
|
|
10
|
+
WithEventBus,
|
|
11
|
+
Schemes,
|
|
12
|
+
} from '@opensumi/ide-core-common';
|
|
13
|
+
import { EditorDocumentModelSavedEvent, EditorDocumentModelWillSaveEvent } from '@opensumi/ide-editor/lib/browser';
|
|
14
|
+
import { IWorkspaceService } from '@opensumi/ide-workspace';
|
|
15
|
+
import {
|
|
16
|
+
FileOperation,
|
|
17
|
+
WorkspaceFileEvent,
|
|
18
|
+
IWorkspaceFileOperationParticipant,
|
|
19
|
+
IWorkspaceFileService,
|
|
20
|
+
} from '@opensumi/ide-workspace-edit';
|
|
21
|
+
|
|
22
|
+
enum ContentUpdateOperation {
|
|
23
|
+
/**
|
|
24
|
+
* 文本内容更新
|
|
25
|
+
*/
|
|
26
|
+
Update = -100,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
type FileAndContentOperation = FileOperation | ContentUpdateOperation;
|
|
30
|
+
|
|
31
|
+
interface FileChangeMarker {
|
|
32
|
+
/**
|
|
33
|
+
* 开始的时间戳
|
|
34
|
+
*/
|
|
35
|
+
start: number;
|
|
36
|
+
/**
|
|
37
|
+
* 标记操作种类
|
|
38
|
+
*/
|
|
39
|
+
operation: FileAndContentOperation;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
enum FileOperationResultEnum {
|
|
43
|
+
SUCCESS = 'success',
|
|
44
|
+
FAIL = 'fail',
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const FileOperationMsgMapStr = {
|
|
48
|
+
[ContentUpdateOperation.Update]: 'update',
|
|
49
|
+
[FileOperation.COPY]: 'copy',
|
|
50
|
+
[FileOperation.CREATE]: 'create',
|
|
51
|
+
[FileOperation.DELETE]: 'delete',
|
|
52
|
+
[FileOperation.MOVE]: 'move',
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const TRACE_LOG_FLAG = 'trace.file-content.update.time';
|
|
56
|
+
|
|
57
|
+
const configurationSchema: PreferenceSchema = {
|
|
58
|
+
title: 'Addons',
|
|
59
|
+
properties: {
|
|
60
|
+
[TRACE_LOG_FLAG]: {
|
|
61
|
+
type: 'boolean',
|
|
62
|
+
default: false,
|
|
63
|
+
description: 'flag for tracing file/content update time consuming',
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* 本模块默认不加载,集成侧需要自定引入
|
|
70
|
+
* 监听文件及内容变更的耗时
|
|
71
|
+
* 命中条件:
|
|
72
|
+
* * file 类型文件
|
|
73
|
+
* * 在当前项目的 workspace root 下的文件
|
|
74
|
+
* * 打开了 trace log 配置 (具体字段见 `TRACE_LOG_FLAG`)
|
|
75
|
+
* 记录的文件操作类型:
|
|
76
|
+
* * 文件的创建/删除/复制/移动
|
|
77
|
+
* * 文件内容变更 (增加 debounce 500ms 避免自动保存/批量替换/LSP重命名 触发的大量变更导致上报数量过多)
|
|
78
|
+
* 记录的字段:
|
|
79
|
+
* * name: `FileAndContentOperation`
|
|
80
|
+
* * msg: 文件操作的类型
|
|
81
|
+
* * extra.uri: 文件 uri string
|
|
82
|
+
* * extra.time: 耗时
|
|
83
|
+
* * extra.result: 0 为成功 1 为失败
|
|
84
|
+
* 其他备注:
|
|
85
|
+
* * time 的实际意义:
|
|
86
|
+
* - 记录了文件操作的从 js 代码开始执行到创建成功后前端可以收到通知的过程时长
|
|
87
|
+
* - 但是不涵盖异步事件通知文件树进行节点变幻到前端渲染完成的耗时部分
|
|
88
|
+
* - 当然对于文件内容更新的操作,这个时间就是完整的时间
|
|
89
|
+
*/
|
|
90
|
+
@Injectable()
|
|
91
|
+
@Domain(ClientAppContribution)
|
|
92
|
+
export class FileAndContentUpdateTimeContribution extends WithEventBus {
|
|
93
|
+
@Autowired(IWorkspaceFileService)
|
|
94
|
+
private readonly workspaceFileService: IWorkspaceFileService;
|
|
95
|
+
|
|
96
|
+
@Autowired(PreferenceService)
|
|
97
|
+
private readonly preferenceService: PreferenceService;
|
|
98
|
+
|
|
99
|
+
@Autowired(IWorkspaceService)
|
|
100
|
+
private readonly workspaceService: IWorkspaceService;
|
|
101
|
+
|
|
102
|
+
@Autowired(IReporterService)
|
|
103
|
+
private readonly reporterService: IReporterService;
|
|
104
|
+
|
|
105
|
+
@Autowired(PreferenceSchemaProvider)
|
|
106
|
+
private readonly preferenceSchemaProvider: PreferenceSchemaProvider;
|
|
107
|
+
|
|
108
|
+
private _traceConfig = false;
|
|
109
|
+
|
|
110
|
+
private _markedFileUris = new StaleLRUMap<string, FileChangeMarker>(100, 50, 10 * 60 * 1000 /* 十分钟超时清理 */);
|
|
111
|
+
|
|
112
|
+
constructor() {
|
|
113
|
+
super();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
onDidStart() {
|
|
117
|
+
// Set configuration schema
|
|
118
|
+
this.preferenceSchemaProvider.setSchema(configurationSchema);
|
|
119
|
+
// Do somethings when file operation is happening
|
|
120
|
+
this._participateFileOperation();
|
|
121
|
+
// Init listener for showPreview
|
|
122
|
+
this._initTraceConfig();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// 增加一个 debounce 避免 editor.autoSave 导致频繁变更触发
|
|
126
|
+
// 只监听 file 协议
|
|
127
|
+
@debounce(500)
|
|
128
|
+
@OnEvent(EditorDocumentModelWillSaveEvent)
|
|
129
|
+
public handleEditorDocModelWillSave(e: EditorDocumentModelWillSaveEvent) {
|
|
130
|
+
const { uri } = e.payload;
|
|
131
|
+
// 手动保存的立刻上报
|
|
132
|
+
// 标记文件链接
|
|
133
|
+
this._markFileUri(uri, ContentUpdateOperation.Update);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
@debounce(500)
|
|
137
|
+
@OnEvent(EditorDocumentModelSavedEvent)
|
|
138
|
+
public handleEditorDocModelDidSave(e: EditorDocumentModelSavedEvent) {
|
|
139
|
+
const uri = e.payload;
|
|
140
|
+
this._reportFileOperation(uri, ContentUpdateOperation.Update, FileOperationResultEnum.SUCCESS);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
private _participateFileOperation() {
|
|
144
|
+
// BEFORE file operation
|
|
145
|
+
this.addDispose(
|
|
146
|
+
this.workspaceFileService.registerFileOperationParticipant({
|
|
147
|
+
participate: this.fileOperationParticipant.bind(this),
|
|
148
|
+
}),
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
// AFTER file operation SUCCEED
|
|
152
|
+
this.addDispose(
|
|
153
|
+
this.workspaceFileService.onDidRunWorkspaceFileOperation(this._handleFileOperationDidRun.bind(this)),
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
// AFTER file operation FAILED
|
|
157
|
+
this.addDispose(
|
|
158
|
+
this.workspaceFileService.onDidFailWorkspaceFileOperation(this._handleFileOperationDidFail.bind(this)),
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
private async fileOperationParticipant(...args: Parameters<IWorkspaceFileOperationParticipant['participate']>) {
|
|
163
|
+
const [files, operation] = args;
|
|
164
|
+
for (const file of files) {
|
|
165
|
+
this._markFileUri(new URI(file.target), operation);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* refactoring changes 带来的文件增删也会带进来,但是一般来说比较少见
|
|
171
|
+
* 出于性能考虑,删除文件的设计是异步操作的,因此删除文件可能耗时比肉眼可见的要长一些
|
|
172
|
+
*/
|
|
173
|
+
private _handleFileOperationDidRun(e: WorkspaceFileEvent) {
|
|
174
|
+
const { files, operation } = e;
|
|
175
|
+
for (const file of files) {
|
|
176
|
+
this._reportFileOperation(new URI(file.target), operation, FileOperationResultEnum.SUCCESS);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
private _handleFileOperationDidFail(e: WorkspaceFileEvent) {
|
|
181
|
+
const { files, operation } = e;
|
|
182
|
+
for (const file of files) {
|
|
183
|
+
this._reportFileOperation(new URI(file.target), operation, FileOperationResultEnum.FAIL);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
private async _markFileUri(uri: URI, operation: FileAndContentOperation) {
|
|
188
|
+
const shouldReport = await this._shouldReport(uri);
|
|
189
|
+
if (!shouldReport) {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const uriStr = uri.toString(true);
|
|
194
|
+
this._markedFileUris.set(uriStr, {
|
|
195
|
+
start: Date.now(),
|
|
196
|
+
operation,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
private async _reportFileOperation(uri: URI, operation: FileAndContentOperation, result: FileOperationResultEnum) {
|
|
201
|
+
const shouldReport = await this._shouldReport(uri);
|
|
202
|
+
if (!shouldReport) {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const uriStr = uri.toString(true);
|
|
207
|
+
const existedMarker = this._markedFileUris.get(uriStr);
|
|
208
|
+
// 不存在标记 | 标记的行为不匹配的 均过滤掉
|
|
209
|
+
if (!existedMarker || existedMarker.operation !== operation) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
// report here
|
|
213
|
+
this.reporterService.point(
|
|
214
|
+
'FileAndContentOperation',
|
|
215
|
+
FileOperationMsgMapStr[existedMarker.operation] || 'unknown',
|
|
216
|
+
{
|
|
217
|
+
uri: uriStr,
|
|
218
|
+
time: Date.now() - existedMarker.start,
|
|
219
|
+
result: result === FileOperationResultEnum.SUCCESS ? 0 : 1,
|
|
220
|
+
},
|
|
221
|
+
);
|
|
222
|
+
// delete cached
|
|
223
|
+
this._markedFileUris.delete(uriStr);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
private _initTraceConfig() {
|
|
227
|
+
// additional edits for file-participants
|
|
228
|
+
this._traceConfig = !!this.preferenceService.get<boolean>(TRACE_LOG_FLAG);
|
|
229
|
+
this.addDispose(
|
|
230
|
+
this.preferenceService.onPreferenceChanged((e) => {
|
|
231
|
+
if (e.preferenceName === TRACE_LOG_FLAG) {
|
|
232
|
+
this._traceConfig = !!e.newValue;
|
|
233
|
+
}
|
|
234
|
+
}),
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* 不符合要求的文件直接跳过
|
|
240
|
+
*/
|
|
241
|
+
private async _shouldReport(uri: URI): Promise<boolean> {
|
|
242
|
+
// 配置项关闭则直接跳过记录
|
|
243
|
+
if (!this._traceConfig) {
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// 非当前 workspace 的文件不统计 | 非 file 协议不统计
|
|
248
|
+
if (uri.scheme !== Schemes.file) {
|
|
249
|
+
return false;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// 文件是当前 workspace 下的
|
|
253
|
+
const roots = await this.workspaceService.roots;
|
|
254
|
+
return roots.some((fileStat) => !!new URI(fileStat.uri).relative(uri));
|
|
255
|
+
}
|
|
256
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Injectable, Autowired } from '@opensumi/di';
|
|
2
|
+
import { ClientAppContribution, Domain } from '@opensumi/ide-core-browser';
|
|
3
|
+
import { OnEvent, FileTreeDropEvent, WithEventBus } from '@opensumi/ide-core-common';
|
|
4
|
+
|
|
5
|
+
import { IFileDropFrontendService, IFileDropFrontendServiceToken } from '../common';
|
|
6
|
+
|
|
7
|
+
@Injectable()
|
|
8
|
+
@Domain(ClientAppContribution)
|
|
9
|
+
export class FileDropContribution extends WithEventBus {
|
|
10
|
+
@Autowired(IFileDropFrontendServiceToken)
|
|
11
|
+
protected readonly dropService: IFileDropFrontendService;
|
|
12
|
+
|
|
13
|
+
@OnEvent(FileTreeDropEvent)
|
|
14
|
+
onDidDropFile(e: FileTreeDropEvent) {
|
|
15
|
+
this.dropService.onDidDropFile(e);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { Injectable, Autowired } from '@opensumi/di';
|
|
2
|
+
import { formatLocalize } from '@opensumi/ide-core-browser';
|
|
3
|
+
import { IStatusBarService, StatusBarAlignment, StatusBarEntryAccessor } from '@opensumi/ide-core-browser/lib/services';
|
|
4
|
+
import { WithEventBus, Uri, path } from '@opensumi/ide-core-common';
|
|
5
|
+
import { FileTreeDropEvent } from '@opensumi/ide-core-common/lib/types/dnd';
|
|
6
|
+
import { IFileServiceClient } from '@opensumi/ide-file-service/lib/common';
|
|
7
|
+
|
|
8
|
+
const { Path } = path;
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
IFileDropFrontendService,
|
|
12
|
+
IFileDropBackendService,
|
|
13
|
+
FileDropServicePath,
|
|
14
|
+
IWebkitDataTransfer,
|
|
15
|
+
IWebkitDataTransferItemEntry,
|
|
16
|
+
} from '../common';
|
|
17
|
+
|
|
18
|
+
@Injectable()
|
|
19
|
+
export class FileDropService extends WithEventBus implements IFileDropFrontendService {
|
|
20
|
+
private pending: Set<string> = new Set();
|
|
21
|
+
|
|
22
|
+
@Autowired(IFileServiceClient)
|
|
23
|
+
protected readonly fs: IFileServiceClient;
|
|
24
|
+
|
|
25
|
+
@Autowired(FileDropServicePath)
|
|
26
|
+
protected readonly dropService: IFileDropBackendService;
|
|
27
|
+
|
|
28
|
+
@Autowired(IStatusBarService)
|
|
29
|
+
protected readonly statusBarService: IStatusBarService;
|
|
30
|
+
|
|
31
|
+
private uploadStatus?: StatusBarEntryAccessor;
|
|
32
|
+
|
|
33
|
+
private onDidUploadFileStart(fullPath: string) {
|
|
34
|
+
this.pending.add(fullPath);
|
|
35
|
+
this.createOrUpdateStatusBar();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
private onDidUploadFileEnd(fullPath: string) {
|
|
39
|
+
this.pending.delete(fullPath);
|
|
40
|
+
this.createOrUpdateStatusBar();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
private createOrUpdateStatusBar(speed?: string) {
|
|
44
|
+
if (this.pending.size === 0) {
|
|
45
|
+
if (this.uploadStatus) {
|
|
46
|
+
this.uploadStatus.dispose();
|
|
47
|
+
this.uploadStatus = undefined;
|
|
48
|
+
}
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const entryId = 'sumi-upload-file-status';
|
|
53
|
+
const message = formatLocalize('workbench.uploadingFiles', this.pending.size, speed || '0 MB');
|
|
54
|
+
const entry = {
|
|
55
|
+
text: message,
|
|
56
|
+
alignment: StatusBarAlignment.RIGHT,
|
|
57
|
+
tooltip: message,
|
|
58
|
+
iconClass: 'kaitian-icon kticon-cloud-server',
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
if (!this.uploadStatus) {
|
|
62
|
+
this.uploadStatus = this.statusBarService.addElement(entryId, entry);
|
|
63
|
+
} else {
|
|
64
|
+
this.uploadStatus.update({ id: entryId, ...entry });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
onDidDropFile(e: FileTreeDropEvent) {
|
|
69
|
+
const {
|
|
70
|
+
payload: { event, targetDir },
|
|
71
|
+
} = e;
|
|
72
|
+
if (!targetDir || !event.dataTransfer?.files || event.dataTransfer.files.length === 0) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const items = (event.dataTransfer as unknown as IWebkitDataTransfer).items;
|
|
77
|
+
|
|
78
|
+
let uploadedBytes = 0;
|
|
79
|
+
const startTime = Date.now();
|
|
80
|
+
const reporter = (uploaded: number) => {
|
|
81
|
+
uploadedBytes += uploaded;
|
|
82
|
+
const now = Date.now();
|
|
83
|
+
const bytesUploadedPerSecond = uploadedBytes / ((now - startTime) / 1000);
|
|
84
|
+
const speed = `${(bytesUploadedPerSecond / 1024 / 1024).toFixed(2)} MB`;
|
|
85
|
+
this.createOrUpdateStatusBar(speed);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
for (const item of items) {
|
|
89
|
+
const entry = item.webkitGetAsEntry();
|
|
90
|
+
this.processFilesEntry(targetDir, entry, reporter);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
private async processFilesEntry(
|
|
95
|
+
targetDir: string,
|
|
96
|
+
entry: IWebkitDataTransferItemEntry,
|
|
97
|
+
reporter: (uploadedByteLength: number) => void,
|
|
98
|
+
) {
|
|
99
|
+
if (entry.isFile) {
|
|
100
|
+
this.processFileEntry(entry, targetDir, reporter);
|
|
101
|
+
} else {
|
|
102
|
+
const folder = Uri.file(new Path(targetDir).join(entry.fullPath).toString()).toString();
|
|
103
|
+
await this.fs.createFolder(folder);
|
|
104
|
+
this.processDirEntry(entry, targetDir, reporter);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
private toBinaryString(uint8Arr: Uint8Array): string {
|
|
109
|
+
let i;
|
|
110
|
+
const length = uint8Arr.length;
|
|
111
|
+
let resultString = '';
|
|
112
|
+
for (i = 0; i < length; i += 1) {
|
|
113
|
+
resultString += String.fromCharCode(uint8Arr[i]);
|
|
114
|
+
}
|
|
115
|
+
return resultString;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
private async doUploadFile(file: File, targetDir: string, reporter: (uploadedByteLength: number) => void) {
|
|
119
|
+
const filePath = new Path(targetDir).join(file.name).toString();
|
|
120
|
+
this.onDidUploadFileStart(filePath.toString());
|
|
121
|
+
await this.fs.createFile(Uri.file(filePath.toString()).toString());
|
|
122
|
+
await this.dropService.$ensureFileExist(file.name, targetDir);
|
|
123
|
+
const reader = file.stream().getReader();
|
|
124
|
+
let res: ReadableStreamReadResult<Uint8Array> = await reader.read();
|
|
125
|
+
while (!res.done) {
|
|
126
|
+
await this.dropService.$writeStream(this.toBinaryString(res.value), file.name, targetDir, res.done);
|
|
127
|
+
reporter(res.value?.byteLength);
|
|
128
|
+
res = await reader.read();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (res.done) {
|
|
132
|
+
this.onDidUploadFileEnd(filePath.toString());
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
private processFileEntry(
|
|
137
|
+
fileEntry: IWebkitDataTransferItemEntry,
|
|
138
|
+
targetDir: string,
|
|
139
|
+
reporter: (uploadedByteLength: number) => void,
|
|
140
|
+
): void {
|
|
141
|
+
fileEntry.file(
|
|
142
|
+
(fileChunk) => {
|
|
143
|
+
const file = new File([fileChunk], fileEntry.fullPath!, { type: fileEntry.type! });
|
|
144
|
+
this.doUploadFile(file, targetDir, reporter);
|
|
145
|
+
},
|
|
146
|
+
() => {},
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
private processDirEntry(
|
|
151
|
+
entry: IWebkitDataTransferItemEntry,
|
|
152
|
+
targetDir: string,
|
|
153
|
+
reporter: (uploadedByteLength: number) => void,
|
|
154
|
+
) {
|
|
155
|
+
const dirReader = entry.createReader();
|
|
156
|
+
dirReader.readEntries(
|
|
157
|
+
(entries) => {
|
|
158
|
+
entries.forEach(async (fileOrDirEntry) => {
|
|
159
|
+
this.processFilesEntry(targetDir, fileOrDirEntry, reporter);
|
|
160
|
+
});
|
|
161
|
+
},
|
|
162
|
+
() => {},
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
}
|