@autonomys/auto-drive 1.0.3 → 1.0.5
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/dist/{cjs/api → api}/calls/download.d.ts +1 -0
- package/dist/api/calls/download.d.ts.map +1 -0
- package/dist/api/calls/download.js +21 -0
- package/dist/{esm/api → api}/calls/index.d.ts +1 -0
- package/dist/api/calls/index.d.ts.map +1 -0
- package/dist/{esm/api → api}/calls/read.d.ts +1 -0
- package/dist/api/calls/read.d.ts.map +1 -0
- package/dist/{cjs/api/calls/read.d.ts → api/calls/read.js} +72 -20
- package/dist/{esm/api → api}/calls/upload.d.ts +4 -3
- package/dist/api/calls/upload.d.ts.map +1 -0
- package/dist/{cjs/api/calls/upload.d.ts → api/calls/upload.js} +65 -29
- package/dist/{cjs/api → api}/calls/write.d.ts +1 -0
- package/dist/api/calls/write.d.ts.map +1 -0
- package/dist/{esm/api/calls/write.d.ts → api/calls/write.js} +38 -12
- package/dist/api/connection.d.ts +15 -0
- package/dist/api/connection.d.ts.map +1 -0
- package/dist/api/connection.js +24 -0
- package/dist/{esm/api → api}/index.d.ts +1 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/models/file.d.ts +8 -0
- package/dist/api/models/file.d.ts.map +1 -0
- package/dist/api/models/file.js +1 -0
- package/dist/api/models/folderTree.d.ts +47 -0
- package/dist/api/models/folderTree.d.ts.map +1 -0
- package/dist/api/models/folderTree.js +103 -0
- package/dist/{esm/api → api}/models/index.d.ts +1 -0
- package/dist/api/models/index.d.ts.map +1 -0
- package/dist/{cjs/api → api}/models/objects.d.ts +1 -0
- package/dist/api/models/objects.d.ts.map +1 -0
- package/dist/api/models/objects.js +10 -0
- package/dist/api/models/uploads.d.ts +130 -0
- package/dist/api/models/uploads.d.ts.map +1 -0
- package/dist/api/models/uploads.js +40 -0
- package/dist/api/wrappers.d.ts +107 -0
- package/dist/api/wrappers.d.ts.map +1 -0
- package/dist/api/wrappers.js +316 -0
- package/dist/{esm/index.d.ts → index.d.ts} +1 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/{cjs/utils → utils}/async.d.ts +2 -0
- package/dist/utils/async.d.ts.map +1 -0
- package/dist/utils/async.js +66 -0
- package/dist/{cjs/utils → utils}/folder.d.ts +1 -0
- package/dist/utils/folder.d.ts.map +1 -0
- package/dist/utils/folder.js +23 -0
- package/dist/{esm/utils → utils}/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/misc.d.ts +2 -0
- package/dist/utils/misc.d.ts.map +1 -0
- package/dist/utils/misc.js +1 -0
- package/dist/utils/observable.d.ts +7 -0
- package/dist/utils/observable.d.ts.map +1 -0
- package/dist/utils/observable.js +15 -0
- package/dist/{cjs/utils → utils}/stream.d.ts +1 -0
- package/dist/utils/stream.d.ts.map +1 -0
- package/dist/utils/stream.js +22 -0
- package/dist/{cjs/utils → utils}/types.d.ts +1 -0
- package/dist/utils/types.d.ts.map +1 -0
- package/dist/utils/types.js +1 -0
- package/package.json +4 -3
- package/src/api/calls/upload.ts +2 -2
- package/src/api/connection.ts +1 -1
- package/src/api/models/file.ts +7 -0
- package/src/api/models/folderTree.ts +51 -0
- package/src/api/models/uploads.ts +17 -1
- package/src/api/wrappers.ts +274 -102
- package/src/utils/async.ts +7 -0
- package/src/utils/misc.ts +1 -0
- package/src/utils/observable.ts +19 -0
- package/dist/cjs/api/connection.d.ts +0 -7
- package/dist/cjs/api/models/folderTree.d.ts +0 -29
- package/dist/cjs/api/models/uploads.d.ts +0 -113
- package/dist/cjs/api/wrappers.d.ts +0 -66
- package/dist/cjs/index.js +0 -28810
- package/dist/cjs/index.js.map +0 -1
- package/dist/esm/api/calls/download.d.ts +0 -5
- package/dist/esm/api/connection.d.ts +0 -7
- package/dist/esm/api/models/folderTree.d.ts +0 -29
- package/dist/esm/api/models/objects.d.ts +0 -40
- package/dist/esm/api/models/uploads.d.ts +0 -113
- package/dist/esm/api/wrappers.d.ts +0 -66
- package/dist/esm/index.js +0 -28762
- package/dist/esm/index.js.map +0 -1
- package/dist/esm/utils/async.d.ts +0 -2
- package/dist/esm/utils/folder.d.ts +0 -1
- package/dist/esm/utils/stream.d.ts +0 -2
- package/dist/esm/utils/types.d.ts +0 -5
- /package/dist/{cjs/api/calls/index.d.ts → api/calls/index.js} +0 -0
- /package/dist/{cjs/api/index.d.ts → api/index.js} +0 -0
- /package/dist/{cjs/api/models/index.d.ts → api/models/index.js} +0 -0
- /package/dist/{cjs/index.d.ts → index.js} +0 -0
- /package/dist/{cjs/utils/index.d.ts → utils/index.js} +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FileUploadOptions } from '@autonomys/auto-dag-data'
|
|
1
|
+
import { CID, FileUploadOptions } from '@autonomys/auto-dag-data'
|
|
2
2
|
import { z } from 'zod'
|
|
3
3
|
import { FolderTreeFolderSchema } from './folderTree.js'
|
|
4
4
|
|
|
@@ -51,3 +51,19 @@ export type FolderUpload = z.infer<typeof folderUploadSchema>
|
|
|
51
51
|
export type CompleteUploadResponse = {
|
|
52
52
|
cid: string
|
|
53
53
|
}
|
|
54
|
+
|
|
55
|
+
export type UploadFileStatus = {
|
|
56
|
+
type: 'file'
|
|
57
|
+
progress: number
|
|
58
|
+
cid?: CID
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export type UploadFolderStatus = {
|
|
62
|
+
type: 'folder'
|
|
63
|
+
progress: number
|
|
64
|
+
cid?: CID
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export type UploadChunksStatus = {
|
|
68
|
+
uploadBytes: number
|
|
69
|
+
}
|
package/src/api/wrappers.ts
CHANGED
|
@@ -10,8 +10,10 @@ import {
|
|
|
10
10
|
} from '@autonomys/auto-dag-data'
|
|
11
11
|
import fs from 'fs'
|
|
12
12
|
import mime from 'mime-types'
|
|
13
|
-
import { asyncByChunk, asyncFromStream } from '../utils/async.js'
|
|
13
|
+
import { asyncByChunk, asyncFromStream, fileToIterable } from '../utils/async.js'
|
|
14
14
|
import { getFiles } from '../utils/folder.js'
|
|
15
|
+
import { progressToPercentage } from '../utils/misc.js'
|
|
16
|
+
import { PromisedObservable } from '../utils/observable.js'
|
|
15
17
|
import {
|
|
16
18
|
completeUpload,
|
|
17
19
|
createFileUpload,
|
|
@@ -22,7 +24,13 @@ import {
|
|
|
22
24
|
uploadFileChunk,
|
|
23
25
|
} from './calls/index.js'
|
|
24
26
|
import { AutoDriveApi } from './connection.js'
|
|
25
|
-
import {
|
|
27
|
+
import { GenericFile } from './models/file.js'
|
|
28
|
+
import {
|
|
29
|
+
constructFromFileSystemEntries,
|
|
30
|
+
constructFromInput,
|
|
31
|
+
constructZipBlobFromTreeAndPaths,
|
|
32
|
+
} from './models/folderTree.js'
|
|
33
|
+
import { UploadChunksStatus, UploadFileStatus, UploadFolderStatus } from './models/uploads.js'
|
|
26
34
|
|
|
27
35
|
type UploadFileOptions = {
|
|
28
36
|
password?: string
|
|
@@ -31,80 +39,135 @@ type UploadFileOptions = {
|
|
|
31
39
|
|
|
32
40
|
const UPLOAD_FILE_CHUNK_SIZE = 1024 * 1024
|
|
33
41
|
|
|
34
|
-
const uploadFileChunks =
|
|
42
|
+
const uploadFileChunks = (
|
|
35
43
|
api: AutoDriveApi,
|
|
36
44
|
fileUploadId: string,
|
|
37
45
|
asyncIterable: AsyncIterable<Buffer>,
|
|
38
46
|
uploadChunkSize: number = UPLOAD_FILE_CHUNK_SIZE,
|
|
39
|
-
) => {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
47
|
+
): PromisedObservable<UploadChunksStatus> => {
|
|
48
|
+
return new PromisedObservable<UploadChunksStatus>(async (subscriber) => {
|
|
49
|
+
let index = 0
|
|
50
|
+
let uploadBytes = 0
|
|
51
|
+
for await (const chunk of asyncByChunk(asyncIterable, uploadChunkSize)) {
|
|
52
|
+
await uploadFileChunk(api, { uploadId: fileUploadId, chunk, index })
|
|
53
|
+
uploadBytes += chunk.length
|
|
54
|
+
subscriber.next({ uploadBytes })
|
|
55
|
+
index++
|
|
56
|
+
}
|
|
57
|
+
subscriber.complete()
|
|
58
|
+
})
|
|
45
59
|
}
|
|
46
60
|
|
|
47
61
|
/**
|
|
48
62
|
* Uploads a file to the server with optional encryption and compression.
|
|
49
63
|
*
|
|
50
64
|
* This function reads a file from the specified file path, optionally encrypts it
|
|
51
|
-
* using the provided password, and compresses it using the
|
|
65
|
+
* using the provided password, and compresses it using the specified algorithm if requested.
|
|
52
66
|
* It then uploads the file in chunks to the server, creating an upload session and
|
|
53
|
-
* completing it once all chunks have been uploaded.
|
|
67
|
+
* completing it once all chunks have been successfully uploaded.
|
|
54
68
|
*
|
|
55
69
|
* @param {AutoDriveApi} api - The API instance used to send requests.
|
|
56
70
|
* @param {string} filePath - The path to the file to be uploaded.
|
|
57
71
|
* @param {UploadFileOptions} options - Options for the upload process.
|
|
58
72
|
* @param {string} [options.password] - The password for encryption (optional).
|
|
59
73
|
* @param {boolean} [options.compression=true] - Whether to compress the file (optional).
|
|
60
|
-
* @
|
|
74
|
+
* @param {number} [uploadChunkSize] - The size of each chunk to upload (optional).
|
|
75
|
+
* @returns {PromisedObservable<UploadFileStatus>} - An observable that emits the upload status.
|
|
61
76
|
* @throws {Error} - Throws an error if the upload fails at any stage.
|
|
62
77
|
*/
|
|
63
|
-
export const
|
|
78
|
+
export const uploadFileFromFilepath = (
|
|
64
79
|
api: AutoDriveApi,
|
|
65
80
|
filePath: string,
|
|
66
81
|
{ password, compression = true }: UploadFileOptions,
|
|
67
82
|
uploadChunkSize?: number,
|
|
68
|
-
):
|
|
69
|
-
|
|
83
|
+
): PromisedObservable<UploadFileStatus> => {
|
|
84
|
+
const name = filePath.split('/').pop()!
|
|
70
85
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
86
|
+
return uploadFileFromInput(
|
|
87
|
+
api,
|
|
88
|
+
{
|
|
89
|
+
read: () => fs.createReadStream(filePath),
|
|
90
|
+
name,
|
|
91
|
+
mimeType: mime.lookup(name) || undefined,
|
|
92
|
+
size: fs.statSync(filePath).size,
|
|
93
|
+
path: filePath,
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
password,
|
|
97
|
+
compression,
|
|
98
|
+
},
|
|
99
|
+
uploadChunkSize,
|
|
100
|
+
)
|
|
101
|
+
}
|
|
77
102
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
103
|
+
/**
|
|
104
|
+
* Uploads a file to the server with optional encryption and compression.
|
|
105
|
+
*
|
|
106
|
+
* This function reads a file from the provided input, optionally encrypts it
|
|
107
|
+
* using the specified password, and compresses it using the specified algorithm if requested.
|
|
108
|
+
* It uploads the file in chunks to the server, creating an upload session and
|
|
109
|
+
* completing it once all chunks have been successfully uploaded.
|
|
110
|
+
*
|
|
111
|
+
* @param {AutoDriveApi} api - The API instance used to send requests.
|
|
112
|
+
* @param {File | GenericFile} file - The file to be uploaded, which can be a File or a GenericFile.
|
|
113
|
+
* @param {UploadFileOptions} options - Options for the upload process.
|
|
114
|
+
* @param {string} [options.password] - The password for encryption (optional).
|
|
115
|
+
* @param {boolean} [options.compression=true] - Whether to compress the file (optional).
|
|
116
|
+
* @param {number} [uploadChunkSize] - The size of each chunk to upload (optional).
|
|
117
|
+
* @returns {Promise<CID>} - A promise that resolves with the CID of the uploaded file.
|
|
118
|
+
* @throws {Error} - Throws an error if the upload fails at any stage.
|
|
119
|
+
*/
|
|
120
|
+
export const uploadFileFromInput = (
|
|
121
|
+
api: AutoDriveApi,
|
|
122
|
+
file: File | GenericFile,
|
|
123
|
+
{ password, compression = true }: UploadFileOptions,
|
|
124
|
+
uploadChunkSize?: number,
|
|
125
|
+
): PromisedObservable<UploadFileStatus> => {
|
|
126
|
+
return new PromisedObservable<UploadFileStatus>(async (subscriber) => {
|
|
127
|
+
let asyncIterable: AsyncIterable<Buffer> =
|
|
128
|
+
file instanceof File ? fileToIterable(file) : file.read()
|
|
83
129
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
: undefined,
|
|
91
|
-
encryption: password
|
|
92
|
-
? {
|
|
93
|
-
algorithm: EncryptionAlgorithm.AES_256_GCM,
|
|
94
|
-
}
|
|
95
|
-
: undefined,
|
|
96
|
-
}
|
|
97
|
-
const fileUpload = await createFileUpload(api, {
|
|
98
|
-
mimeType: mime.lookup(filePath) || undefined,
|
|
99
|
-
filename: filePath.split('/').pop()!,
|
|
100
|
-
uploadOptions,
|
|
101
|
-
})
|
|
130
|
+
if (compression) {
|
|
131
|
+
asyncIterable = compressFile(asyncIterable, {
|
|
132
|
+
level: 9,
|
|
133
|
+
algorithm: CompressionAlgorithm.ZLIB,
|
|
134
|
+
})
|
|
135
|
+
}
|
|
102
136
|
|
|
103
|
-
|
|
137
|
+
if (password) {
|
|
138
|
+
asyncIterable = encryptFile(asyncIterable, password, {
|
|
139
|
+
algorithm: EncryptionAlgorithm.AES_256_GCM,
|
|
140
|
+
})
|
|
141
|
+
}
|
|
104
142
|
|
|
105
|
-
|
|
143
|
+
const uploadOptions = {
|
|
144
|
+
compression: compression
|
|
145
|
+
? {
|
|
146
|
+
level: 9,
|
|
147
|
+
algorithm: CompressionAlgorithm.ZLIB,
|
|
148
|
+
}
|
|
149
|
+
: undefined,
|
|
150
|
+
encryption: password
|
|
151
|
+
? {
|
|
152
|
+
algorithm: EncryptionAlgorithm.AES_256_GCM,
|
|
153
|
+
}
|
|
154
|
+
: undefined,
|
|
155
|
+
}
|
|
156
|
+
const fileUpload = await createFileUpload(api, {
|
|
157
|
+
mimeType: mime.lookup(file.name) || undefined,
|
|
158
|
+
filename: file.name,
|
|
159
|
+
uploadOptions,
|
|
160
|
+
})
|
|
106
161
|
|
|
107
|
-
|
|
162
|
+
await uploadFileChunks(api, fileUpload.id, asyncIterable, uploadChunkSize).forEach((e) =>
|
|
163
|
+
subscriber.next({ type: 'file', progress: progressToPercentage(e.uploadBytes, file.size) }),
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
const result = await completeUpload(api, { uploadId: fileUpload.id })
|
|
167
|
+
|
|
168
|
+
subscriber.next({ type: 'file', progress: 100, cid: stringToCid(result.cid) })
|
|
169
|
+
subscriber.complete()
|
|
170
|
+
})
|
|
108
171
|
}
|
|
109
172
|
|
|
110
173
|
/**
|
|
@@ -112,40 +175,170 @@ export const uploadFile = async (
|
|
|
112
175
|
*
|
|
113
176
|
* This function retrieves all files within the specified folder,
|
|
114
177
|
* constructs a file tree representation, and initiates the upload
|
|
115
|
-
* process. It also handles optional compression of the files during
|
|
178
|
+
* process. It also handles optional compression and encryption of the files during
|
|
116
179
|
* the upload.
|
|
117
180
|
*
|
|
181
|
+
* If a password is provided, the files will be zipped before uploading.
|
|
182
|
+
*
|
|
118
183
|
* @param {AutoDriveApi} api - The API instance used to send requests.
|
|
119
184
|
* @param {string} folderPath - The path of the folder to be uploaded.
|
|
120
|
-
* @
|
|
185
|
+
* @param {Object} options - Optional parameters for the upload.
|
|
186
|
+
* @param {number} [options.uploadChunkSize] - The size of each chunk to be uploaded.
|
|
187
|
+
* @param {string} [options.password] - An optional password for encrypting the files.
|
|
188
|
+
* @returns {Promise<PromisedObservable<UploadFileStatus | UploadFolderStatus>>} - A promise that resolves to an observable that tracks the upload progress.
|
|
121
189
|
* @throws {Error} - Throws an error if the upload fails at any stage.
|
|
122
190
|
*/
|
|
123
|
-
export const
|
|
191
|
+
export const uploadFolderFromFolderPath = async (
|
|
124
192
|
api: AutoDriveApi,
|
|
125
193
|
folderPath: string,
|
|
126
|
-
uploadChunkSize?: number,
|
|
127
|
-
): Promise<
|
|
194
|
+
{ uploadChunkSize, password }: { uploadChunkSize?: number; password?: string } = {},
|
|
195
|
+
): Promise<PromisedObservable<UploadFileStatus | UploadFolderStatus>> => {
|
|
128
196
|
const files = await getFiles(folderPath)
|
|
129
|
-
|
|
130
197
|
const fileTree = constructFromFileSystemEntries(files)
|
|
131
198
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
199
|
+
if (password) {
|
|
200
|
+
const filesMap = Object.fromEntries(files.map((file) => [file, file]))
|
|
201
|
+
const zipBlob = await constructZipBlobFromTreeAndPaths(fileTree, filesMap)
|
|
202
|
+
const name = folderPath.split('/').pop()!
|
|
203
|
+
return uploadFileFromInput(
|
|
204
|
+
api,
|
|
205
|
+
{
|
|
206
|
+
read: () => fileToIterable(zipBlob),
|
|
207
|
+
name,
|
|
208
|
+
mimeType: 'application/zip',
|
|
209
|
+
size: zipBlob.size,
|
|
210
|
+
path: name,
|
|
138
211
|
},
|
|
139
|
-
|
|
212
|
+
{
|
|
213
|
+
password,
|
|
214
|
+
compression: true,
|
|
215
|
+
},
|
|
216
|
+
)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return new PromisedObservable<UploadFolderStatus>(async (subscriber) => {
|
|
220
|
+
const folderUpload = await createFolderUpload(api, {
|
|
221
|
+
fileTree,
|
|
222
|
+
uploadOptions: {
|
|
223
|
+
compression: {
|
|
224
|
+
algorithm: CompressionAlgorithm.ZLIB,
|
|
225
|
+
level: 9,
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
const genericFiles: GenericFile[] = files.map((file) => ({
|
|
231
|
+
read: () => fs.createReadStream(file),
|
|
232
|
+
name: file.split('/').pop()!,
|
|
233
|
+
mimeType: mime.lookup(file.split('/').pop()!) || undefined,
|
|
234
|
+
size: fs.statSync(file).size,
|
|
235
|
+
path: file,
|
|
236
|
+
}))
|
|
237
|
+
|
|
238
|
+
const totalSize = genericFiles.reduce((acc, file) => acc + file.size, 0)
|
|
239
|
+
|
|
240
|
+
let progress = 0
|
|
241
|
+
for (const file of genericFiles) {
|
|
242
|
+
await uploadFileWithinFolderUpload(api, folderUpload.id, file, uploadChunkSize).forEach((e) =>
|
|
243
|
+
subscriber.next({
|
|
244
|
+
type: 'folder',
|
|
245
|
+
progress: progressToPercentage(progress + e.uploadBytes, totalSize),
|
|
246
|
+
}),
|
|
247
|
+
)
|
|
248
|
+
progress += file.size
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const result = await completeUpload(api, { uploadId: folderUpload.id })
|
|
252
|
+
|
|
253
|
+
subscriber.next({ type: 'folder', progress: 100, cid: stringToCid(result.cid) })
|
|
254
|
+
subscriber.complete()
|
|
140
255
|
})
|
|
256
|
+
}
|
|
141
257
|
|
|
142
|
-
|
|
143
|
-
|
|
258
|
+
/**
|
|
259
|
+
* Uploads an entire folder to the server.
|
|
260
|
+
*
|
|
261
|
+
* This function retrieves all files within the specified folder,
|
|
262
|
+
* constructs a file tree representation, and initiates the upload
|
|
263
|
+
* process. It also handles optional compression of the files during
|
|
264
|
+
* the upload. If a password is provided, the files will be zipped
|
|
265
|
+
* before uploading.
|
|
266
|
+
*
|
|
267
|
+
* @param {AutoDriveApi} api - The API instance used to send requests.
|
|
268
|
+
* @param {FileList | File[]} fileList - The list of files to be uploaded.
|
|
269
|
+
* @param {Object} options - Options for the upload process.
|
|
270
|
+
* @param {number} [options.uploadChunkSize] - The size of each chunk to upload (optional).
|
|
271
|
+
* @param {string} [options.password] - The password for encryption (optional).
|
|
272
|
+
* @returns {PromisedObservable<UploadFileStatus | UploadFolderStatus>} - An observable that emits the upload status.
|
|
273
|
+
* @throws {Error} - Throws an error if the upload fails at any stage.
|
|
274
|
+
*/
|
|
275
|
+
export const uploadFolderFromInput = async (
|
|
276
|
+
api: AutoDriveApi,
|
|
277
|
+
fileList: FileList | File[],
|
|
278
|
+
{ uploadChunkSize, password }: { uploadChunkSize?: number; password?: string } = {},
|
|
279
|
+
): Promise<PromisedObservable<UploadFileStatus | UploadFolderStatus>> => {
|
|
280
|
+
const files = fileList instanceof FileList ? Array.from(fileList) : fileList
|
|
281
|
+
const fileTree = constructFromInput(files)
|
|
282
|
+
|
|
283
|
+
// If password is provided, we zip the files and upload the zip file
|
|
284
|
+
if (password) {
|
|
285
|
+
const filesMap: Record<string, File> = Object.fromEntries(
|
|
286
|
+
files.map((file) => [file.webkitRelativePath, file]),
|
|
287
|
+
)
|
|
288
|
+
const zipBlob = await constructZipBlobFromTreeAndPaths(fileTree, filesMap)
|
|
289
|
+
const name = fileList[0].webkitRelativePath.split('/').filter(Boolean)[0]!
|
|
290
|
+
|
|
291
|
+
return uploadFileFromInput(
|
|
292
|
+
api,
|
|
293
|
+
{
|
|
294
|
+
read: () => fileToIterable(zipBlob),
|
|
295
|
+
name: `${name}.zip`,
|
|
296
|
+
mimeType: 'application/zip',
|
|
297
|
+
size: zipBlob.size,
|
|
298
|
+
path: name,
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
password,
|
|
302
|
+
compression: true,
|
|
303
|
+
},
|
|
304
|
+
)
|
|
144
305
|
}
|
|
145
306
|
|
|
146
|
-
|
|
307
|
+
return new PromisedObservable<UploadFolderStatus>(async (subscriber) => {
|
|
308
|
+
// Otherwise, we upload the files as a folder w/o compression or encryption
|
|
309
|
+
const folderUpload = await createFolderUpload(api, {
|
|
310
|
+
fileTree,
|
|
311
|
+
})
|
|
312
|
+
|
|
313
|
+
let currentBytesUploaded = 0
|
|
314
|
+
const totalSize = files.reduce((acc, file) => acc + file.size, 0)
|
|
315
|
+
for (const file of files) {
|
|
316
|
+
await uploadFileWithinFolderUpload(
|
|
317
|
+
api,
|
|
318
|
+
folderUpload.id,
|
|
319
|
+
{
|
|
320
|
+
read: () => fileToIterable(file),
|
|
321
|
+
name: file.name,
|
|
322
|
+
mimeType: mime.lookup(file.name) || undefined,
|
|
323
|
+
size: file.size,
|
|
324
|
+
path: file.webkitRelativePath,
|
|
325
|
+
},
|
|
326
|
+
uploadChunkSize,
|
|
327
|
+
).forEach((e) => {
|
|
328
|
+
subscriber.next({
|
|
329
|
+
type: 'folder',
|
|
330
|
+
progress: progressToPercentage(currentBytesUploaded + e.uploadBytes, totalSize),
|
|
331
|
+
})
|
|
332
|
+
})
|
|
333
|
+
|
|
334
|
+
currentBytesUploaded += file.size
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const result = await completeUpload(api, { uploadId: folderUpload.id })
|
|
147
338
|
|
|
148
|
-
|
|
339
|
+
subscriber.next({ type: 'folder', progress: 100, cid: stringToCid(result.cid) })
|
|
340
|
+
subscriber.complete()
|
|
341
|
+
})
|
|
149
342
|
}
|
|
150
343
|
|
|
151
344
|
/**
|
|
@@ -157,26 +350,29 @@ export const uploadFolder = async (
|
|
|
157
350
|
*
|
|
158
351
|
* @returns {Promise<void>} A promise that resolves when the file upload is complete.
|
|
159
352
|
*/
|
|
160
|
-
export const uploadFileWithinFolderUpload =
|
|
353
|
+
export const uploadFileWithinFolderUpload = (
|
|
161
354
|
api: AutoDriveApi,
|
|
162
355
|
uploadId: string,
|
|
163
|
-
|
|
356
|
+
file: GenericFile,
|
|
164
357
|
uploadChunkSize?: number,
|
|
165
|
-
):
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
358
|
+
): PromisedObservable<UploadChunksStatus> => {
|
|
359
|
+
return new PromisedObservable<UploadChunksStatus>(async (subscriber) => {
|
|
360
|
+
const fileUpload = await createFileUploadWithinFolderUpload(api, {
|
|
361
|
+
uploadId,
|
|
362
|
+
name: file.name,
|
|
363
|
+
mimeType: file.mimeType,
|
|
364
|
+
relativeId: file.path,
|
|
365
|
+
uploadOptions: {},
|
|
366
|
+
})
|
|
174
367
|
|
|
175
|
-
|
|
368
|
+
await uploadFileChunks(api, fileUpload.id, file.read(), uploadChunkSize).forEach((e) =>
|
|
369
|
+
subscriber.next({ uploadBytes: e.uploadBytes }),
|
|
370
|
+
)
|
|
176
371
|
|
|
177
|
-
|
|
372
|
+
await completeUpload(api, { uploadId: fileUpload.id })
|
|
178
373
|
|
|
179
|
-
|
|
374
|
+
subscriber.complete()
|
|
375
|
+
})
|
|
180
376
|
}
|
|
181
377
|
|
|
182
378
|
/**
|
|
@@ -211,27 +407,3 @@ export const downloadFile = async (
|
|
|
211
407
|
|
|
212
408
|
return iterable
|
|
213
409
|
}
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* Downloads a folder from the AutoDrive service without encryption.
|
|
217
|
-
*
|
|
218
|
-
* @param {AutoDriveApi} api - The API instance to interact with the AutoDrive service.
|
|
219
|
-
* @param {string} cid - The CID of the folder to be downloaded.
|
|
220
|
-
*
|
|
221
|
-
* @returns {Promise<ReadableStream<Uint8Array>>} A promise that resolves to a ReadableStream of the downloaded folder.
|
|
222
|
-
*
|
|
223
|
-
* @warning If the folder is encrypted, a warning will be logged, but the download will proceed without decryption.
|
|
224
|
-
*/
|
|
225
|
-
export const downloadFolderWithoutEncryption = async (
|
|
226
|
-
api: AutoDriveApi,
|
|
227
|
-
cid: string,
|
|
228
|
-
): Promise<ReadableStream<Uint8Array>> => {
|
|
229
|
-
const metadata = await getObjectMetadata(api, { cid })
|
|
230
|
-
if (metadata.uploadOptions?.encryption) {
|
|
231
|
-
console.warn(
|
|
232
|
-
'Downloading encrypted folder. Folder support encryption but it is not recommended.',
|
|
233
|
-
)
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
return downloadObject(api, { cid })
|
|
237
|
-
}
|
package/src/utils/async.ts
CHANGED
|
@@ -27,3 +27,10 @@ export const asyncFromStream = async function* (
|
|
|
27
27
|
result = await reader.read()
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
|
+
|
|
31
|
+
export const fileToIterable = async function* (file: File | Blob): AsyncIterable<Buffer> {
|
|
32
|
+
const chunkSize = 1024 * 1024
|
|
33
|
+
for (let i = 0; i < file.size; i += chunkSize) {
|
|
34
|
+
yield Buffer.from(await file.slice(i, i + chunkSize).arrayBuffer())
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const progressToPercentage = (current: number, target: number) => (current / target) * 100
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { firstValueFrom, lastValueFrom, Observable, Subscriber } from 'rxjs'
|
|
2
|
+
|
|
3
|
+
const asyncCallback = <T, O>(callback: (t: T) => O) => {
|
|
4
|
+
return (t: T) => {
|
|
5
|
+
callback(t)
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export class PromisedObservable<T> extends Observable<T> {
|
|
10
|
+
constructor(subscribe?: (this: Observable<T>, subscriber: Subscriber<T>) => void) {
|
|
11
|
+
super(subscribe && asyncCallback(subscribe))
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
get promise(): Promise<T> {
|
|
15
|
+
return lastValueFrom(this)
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export { firstValueFrom, lastValueFrom }
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
export type FolderTreeFolder = {
|
|
3
|
-
name: string;
|
|
4
|
-
type: 'folder';
|
|
5
|
-
children: FolderTree[];
|
|
6
|
-
id: string;
|
|
7
|
-
};
|
|
8
|
-
export type FolderTreeFile = {
|
|
9
|
-
name: string;
|
|
10
|
-
type: 'file';
|
|
11
|
-
id: string;
|
|
12
|
-
};
|
|
13
|
-
export type FolderTree = FolderTreeFolder | FolderTreeFile;
|
|
14
|
-
export declare const FolderTreeFolderSchema: any;
|
|
15
|
-
export declare const FolderTreeFileSchema: z.ZodObject<{
|
|
16
|
-
name: z.ZodString;
|
|
17
|
-
type: z.ZodLiteral<"file">;
|
|
18
|
-
id: z.ZodString;
|
|
19
|
-
}, "strip", z.ZodTypeAny, {
|
|
20
|
-
name?: string;
|
|
21
|
-
type?: "file";
|
|
22
|
-
id?: string;
|
|
23
|
-
}, {
|
|
24
|
-
name?: string;
|
|
25
|
-
type?: "file";
|
|
26
|
-
id?: string;
|
|
27
|
-
}>;
|
|
28
|
-
export declare const FolderTreeSchema: any;
|
|
29
|
-
export declare const constructFromFileSystemEntries: (entries: string[]) => FolderTree;
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import { FileUploadOptions } from '@autonomys/auto-dag-data';
|
|
2
|
-
import { z } from 'zod';
|
|
3
|
-
export declare enum UploadType {
|
|
4
|
-
FILE = "file",
|
|
5
|
-
FOLDER = "folder"
|
|
6
|
-
}
|
|
7
|
-
export declare enum UploadStatus {
|
|
8
|
-
PENDING = "pending",
|
|
9
|
-
COMPLETED = "completed",
|
|
10
|
-
MIGRATING = "migrating",
|
|
11
|
-
CANCELLED = "cancelled",
|
|
12
|
-
FAILED = "failed"
|
|
13
|
-
}
|
|
14
|
-
export declare const fileUploadSchema: z.ZodObject<{
|
|
15
|
-
id: z.ZodString;
|
|
16
|
-
rootId: z.ZodString;
|
|
17
|
-
relativeId: z.ZodNullable<z.ZodString>;
|
|
18
|
-
type: z.ZodNativeEnum<typeof UploadType>;
|
|
19
|
-
status: z.ZodNativeEnum<typeof UploadStatus>;
|
|
20
|
-
fileTree: z.ZodNull;
|
|
21
|
-
name: z.ZodString;
|
|
22
|
-
mimeType: z.ZodNullable<z.ZodString>;
|
|
23
|
-
oauthProvider: z.ZodString;
|
|
24
|
-
oauthUserId: z.ZodString;
|
|
25
|
-
}, "strip", z.ZodTypeAny, {
|
|
26
|
-
id?: string;
|
|
27
|
-
rootId?: string;
|
|
28
|
-
relativeId?: string;
|
|
29
|
-
type?: UploadType;
|
|
30
|
-
status?: UploadStatus;
|
|
31
|
-
fileTree?: null;
|
|
32
|
-
name?: string;
|
|
33
|
-
mimeType?: string;
|
|
34
|
-
oauthProvider?: string;
|
|
35
|
-
oauthUserId?: string;
|
|
36
|
-
}, {
|
|
37
|
-
id?: string;
|
|
38
|
-
rootId?: string;
|
|
39
|
-
relativeId?: string;
|
|
40
|
-
type?: UploadType;
|
|
41
|
-
status?: UploadStatus;
|
|
42
|
-
fileTree?: null;
|
|
43
|
-
name?: string;
|
|
44
|
-
mimeType?: string;
|
|
45
|
-
oauthProvider?: string;
|
|
46
|
-
oauthUserId?: string;
|
|
47
|
-
}>;
|
|
48
|
-
export type FileUpload = z.infer<typeof fileUploadSchema> & {
|
|
49
|
-
uploadOptions: FileUploadOptions | null;
|
|
50
|
-
};
|
|
51
|
-
export declare const folderUploadSchema: z.ZodObject<{
|
|
52
|
-
id: z.ZodString;
|
|
53
|
-
rootId: z.ZodString;
|
|
54
|
-
relativeId: z.ZodNullable<z.ZodString>;
|
|
55
|
-
type: z.ZodNativeEnum<typeof UploadType>;
|
|
56
|
-
status: z.ZodNativeEnum<typeof UploadStatus>;
|
|
57
|
-
fileTree: z.ZodObject<{
|
|
58
|
-
name: z.ZodString;
|
|
59
|
-
type: z.ZodLiteral<"folder">;
|
|
60
|
-
children: z.ZodArray<z.ZodLazy<any>, "many">;
|
|
61
|
-
id: z.ZodString;
|
|
62
|
-
}, "strip", z.ZodTypeAny, {
|
|
63
|
-
id?: string;
|
|
64
|
-
type?: "folder";
|
|
65
|
-
name?: string;
|
|
66
|
-
children?: any[];
|
|
67
|
-
}, {
|
|
68
|
-
id?: string;
|
|
69
|
-
type?: "folder";
|
|
70
|
-
name?: string;
|
|
71
|
-
children?: any[];
|
|
72
|
-
}>;
|
|
73
|
-
name: z.ZodString;
|
|
74
|
-
mimeType: z.ZodNull;
|
|
75
|
-
oauthProvider: z.ZodString;
|
|
76
|
-
oauthUserId: z.ZodString;
|
|
77
|
-
uploadOptions: z.ZodNull;
|
|
78
|
-
}, "strip", z.ZodTypeAny, {
|
|
79
|
-
id?: string;
|
|
80
|
-
rootId?: string;
|
|
81
|
-
relativeId?: string;
|
|
82
|
-
type?: UploadType;
|
|
83
|
-
status?: UploadStatus;
|
|
84
|
-
fileTree?: {
|
|
85
|
-
id?: string;
|
|
86
|
-
type?: "folder";
|
|
87
|
-
name?: string;
|
|
88
|
-
children?: any[];
|
|
89
|
-
};
|
|
90
|
-
name?: string;
|
|
91
|
-
mimeType?: null;
|
|
92
|
-
oauthProvider?: string;
|
|
93
|
-
oauthUserId?: string;
|
|
94
|
-
uploadOptions?: null;
|
|
95
|
-
}, {
|
|
96
|
-
id?: string;
|
|
97
|
-
rootId?: string;
|
|
98
|
-
relativeId?: string;
|
|
99
|
-
type?: UploadType;
|
|
100
|
-
status?: UploadStatus;
|
|
101
|
-
fileTree?: {
|
|
102
|
-
id?: string;
|
|
103
|
-
type?: "folder";
|
|
104
|
-
name?: string;
|
|
105
|
-
children?: any[];
|
|
106
|
-
};
|
|
107
|
-
name?: string;
|
|
108
|
-
mimeType?: null;
|
|
109
|
-
oauthProvider?: string;
|
|
110
|
-
oauthUserId?: string;
|
|
111
|
-
uploadOptions?: null;
|
|
112
|
-
}>;
|
|
113
|
-
export type FolderUpload = z.infer<typeof folderUploadSchema>;
|