aliendrive-sdk 1.0.0 → 1.1.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/aliendrive.js +84 -5
- package/package.json +1 -1
package/aliendrive.js
CHANGED
|
@@ -124,24 +124,103 @@ class AlienDrive {
|
|
|
124
124
|
return { DeleteMarker: true };
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
+
// ── Direct-to-B2 Upload (for large files, bypasses server) ──
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Upload directly to B2 storage — bypasses server for the file data.
|
|
131
|
+
* Use this for large files. The flow:
|
|
132
|
+
* 1. Get signed upload URL from server
|
|
133
|
+
* 2. Upload file directly to B2
|
|
134
|
+
* 3. Confirm upload with server (validates size, type, quota)
|
|
135
|
+
*
|
|
136
|
+
* @param {Object} params
|
|
137
|
+
* @param {string} params.fileName - Original file name
|
|
138
|
+
* @param {Buffer} params.body - File content
|
|
139
|
+
* @param {string} [params.contentType] - MIME type
|
|
140
|
+
* @param {string} [params.parentId] - Parent folder ID
|
|
141
|
+
*/
|
|
142
|
+
async directUpload({ fileName, body, contentType = 'application/octet-stream', parentId = null }) {
|
|
143
|
+
const crypto = require('crypto');
|
|
144
|
+
|
|
145
|
+
// Step 1: Get upload URL
|
|
146
|
+
const urlRes = await this._fetch('/api/v1/upload/url', {
|
|
147
|
+
method: 'POST',
|
|
148
|
+
headers: { 'Content-Type': 'application/json' },
|
|
149
|
+
body: JSON.stringify({ fileName, contentType, size: body.length, parentId }),
|
|
150
|
+
});
|
|
151
|
+
if (!urlRes.ok) {
|
|
152
|
+
const err = await urlRes.json().catch(() => ({ error: urlRes.statusText }));
|
|
153
|
+
throw new Error(`Upload URL failed (${urlRes.status}): ${err.error}`);
|
|
154
|
+
}
|
|
155
|
+
const urlData = await urlRes.json();
|
|
156
|
+
|
|
157
|
+
// Step 2: Upload directly to B2
|
|
158
|
+
const sha1 = crypto.createHash('sha1').update(body).digest('hex');
|
|
159
|
+
const b2Res = await fetch(urlData.uploadUrl, {
|
|
160
|
+
method: 'POST',
|
|
161
|
+
headers: {
|
|
162
|
+
'Authorization': urlData.authToken,
|
|
163
|
+
'X-Bz-File-Name': encodeURIComponent(urlData.b2FileName),
|
|
164
|
+
'Content-Type': contentType,
|
|
165
|
+
'Content-Length': String(body.length),
|
|
166
|
+
'X-Bz-Content-Sha1': sha1,
|
|
167
|
+
},
|
|
168
|
+
body: body,
|
|
169
|
+
});
|
|
170
|
+
if (!b2Res.ok) {
|
|
171
|
+
const err = await b2Res.text();
|
|
172
|
+
throw new Error(`B2 upload failed (${b2Res.status}): ${err}`);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Step 3: Confirm with server (validates size, type, quota)
|
|
176
|
+
const confirmRes = await this._fetch('/api/v1/upload/confirm', {
|
|
177
|
+
method: 'POST',
|
|
178
|
+
headers: { 'Content-Type': 'application/json' },
|
|
179
|
+
body: JSON.stringify({
|
|
180
|
+
fileKey: urlData.fileKey,
|
|
181
|
+
fileName,
|
|
182
|
+
size: body.length,
|
|
183
|
+
contentType,
|
|
184
|
+
parentId: urlData.parentId,
|
|
185
|
+
}),
|
|
186
|
+
});
|
|
187
|
+
if (!confirmRes.ok) {
|
|
188
|
+
const err = await confirmRes.json().catch(() => ({ error: confirmRes.statusText }));
|
|
189
|
+
throw new Error(`Upload confirm failed (${confirmRes.status}): ${err.error}`);
|
|
190
|
+
}
|
|
191
|
+
return confirmRes.json();
|
|
192
|
+
}
|
|
193
|
+
|
|
127
194
|
// ── Convenience methods ──
|
|
128
195
|
|
|
129
196
|
/**
|
|
130
|
-
* Upload a local file
|
|
197
|
+
* Upload a local file.
|
|
198
|
+
* Uses direct-to-B2 upload for files > 5MB, S3 API for smaller files.
|
|
131
199
|
*/
|
|
132
200
|
async uploadFile(localPath, remotePath) {
|
|
133
201
|
const fs = require('fs');
|
|
134
202
|
const path = require('path');
|
|
135
203
|
const mime = {
|
|
136
|
-
'.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.png': 'image/png', '.gif': 'image/gif',
|
|
137
|
-
'.pdf': 'application/pdf', '.txt': 'text/plain', '.html': 'text/html',
|
|
204
|
+
'.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.png': 'image/png', '.gif': 'image/gif', '.webp': 'image/webp',
|
|
205
|
+
'.pdf': 'application/pdf', '.txt': 'text/plain', '.html': 'text/html', '.css': 'text/css',
|
|
138
206
|
'.json': 'application/json', '.xml': 'application/xml',
|
|
139
|
-
'.zip': 'application/zip', '.
|
|
207
|
+
'.zip': 'application/zip', '.gz': 'application/gzip',
|
|
208
|
+
'.mp4': 'video/mp4', '.webm': 'video/webm', '.mov': 'video/quicktime',
|
|
209
|
+
'.mp3': 'audio/mpeg', '.wav': 'audio/wav', '.ogg': 'audio/ogg',
|
|
210
|
+
'.doc': 'application/msword', '.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
211
|
+
'.xls': 'application/vnd.ms-excel', '.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
140
212
|
};
|
|
141
213
|
const ext = path.extname(localPath).toLowerCase();
|
|
142
214
|
const body = fs.readFileSync(localPath);
|
|
143
215
|
const key = remotePath || path.basename(localPath);
|
|
144
|
-
|
|
216
|
+
const contentType = mime[ext] || 'application/octet-stream';
|
|
217
|
+
|
|
218
|
+
// Use direct upload for files > 5MB
|
|
219
|
+
if (body.length > 5 * 1024 * 1024) {
|
|
220
|
+
return this.directUpload({ fileName: path.basename(localPath), body, contentType, parentId: null });
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return this.putObject({ Key: key, Body: body, ContentType: contentType });
|
|
145
224
|
}
|
|
146
225
|
|
|
147
226
|
/**
|