Skip to content
This repository was archived by the owner on Mar 19, 2025. It is now read-only.

Commit cbf3176

Browse files
feat: Support cnpm nfs with aws s3 sdk
The following services may be available: 1. Aliyun OSS 2. Tencent Cloud COS 3. AWS S3
1 parent a1a3204 commit cbf3176

File tree

1 file changed

+179
-0
lines changed

1 file changed

+179
-0
lines changed

src/index.ts

+179
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
import * as fs from 'fs'
2+
import * as path from 'path'
3+
import ReadableStream = NodeJS.ReadableStream
4+
import { S3 } from 'aws-sdk'
5+
6+
interface S3Config {
7+
mode: 'public' | 'private'
8+
accessKeyId: string
9+
secretAccessKey: string
10+
bucket: string
11+
region: string
12+
endpoint: string
13+
}
14+
15+
interface UploadOptions {
16+
key: string
17+
size: number
18+
shasum?: string
19+
shasum256?: string
20+
}
21+
22+
interface DownloadOptions {
23+
timeout: number
24+
}
25+
26+
function trimKey(key: string) {
27+
return key ? key.replace(/^\//, '') : ''
28+
}
29+
30+
class S3Wrapper {
31+
s3: S3
32+
config: S3Config
33+
34+
constructor(config: S3Config) {
35+
this.config = config
36+
37+
this.s3 = new S3({
38+
accessKeyId: config.accessKeyId,
39+
secretAccessKey: config.secretAccessKey,
40+
region: config.region,
41+
endpoint: config.endpoint,
42+
})
43+
}
44+
45+
private async _upload(
46+
file: string | ReadableStream,
47+
options: UploadOptions,
48+
): Promise<any> {
49+
const key = trimKey(options.key)
50+
51+
if (typeof file === 'string') {
52+
file = fs.createReadStream(file)
53+
}
54+
55+
try {
56+
this.s3
57+
.putObject({
58+
Bucket: this.config.bucket,
59+
Key: options.key,
60+
Body: file,
61+
})
62+
63+
if (this.config.mode === 'public') {
64+
return {
65+
url: `https://${this.config.bucket}.${this.config.endpoint}/${key}`,
66+
}
67+
} else {
68+
return { key: key }
69+
}
70+
} catch (e) {
71+
throw new Error(`Could not upload file to S3: ${e.message}`)
72+
}
73+
}
74+
75+
async upload(filePath: string, options: UploadOptions): Promise<any> {
76+
return this._upload(filePath, options)
77+
}
78+
79+
async uploadBuffer(
80+
fileBuffer: ReadableStream,
81+
options: UploadOptions,
82+
): Promise<any> {
83+
return this._upload(fileBuffer, options)
84+
}
85+
86+
async remove(key: string): Promise<any> {
87+
const decodedKey = decodeURIComponent(trimKey(key))
88+
89+
this.s3
90+
.headObject({
91+
Bucket: this.config.bucket,
92+
Key: decodedKey,
93+
})
94+
.promise()
95+
.then(() => {
96+
this.s3.deleteObject({
97+
Bucket: this.config.bucket,
98+
Key: decodedKey,
99+
})
100+
})
101+
.catch(e => {
102+
if (e.statusCode === 404) {
103+
e.message = 'File Not Found'
104+
}
105+
throw new Error(`Could not remove file from S3: ${e.message}`)
106+
})
107+
}
108+
109+
async download(
110+
key: string,
111+
savePath: string,
112+
options: DownloadOptions,
113+
): Promise<any> {
114+
const decodedKey = decodeURIComponent(trimKey(key))
115+
116+
const folderPath = path.dirname(savePath)
117+
if (!fs.existsSync(folderPath)) {
118+
fs.mkdirSync(folderPath)
119+
}
120+
121+
this.s3
122+
.headObject({
123+
Bucket: this.config.bucket,
124+
Key: decodedKey,
125+
})
126+
.promise()
127+
.then(() => {
128+
this.s3
129+
.getObject({
130+
Bucket: this.config.bucket,
131+
Key: decodedKey,
132+
})
133+
.createReadStream()
134+
.pipe(fs.createWriteStream(savePath))
135+
})
136+
.catch(e => {
137+
if (e.statusCode === 404) {
138+
e.message = 'File Not Found'
139+
}
140+
throw new Error(`Could not retrieve file from S3: ${e.message}`)
141+
})
142+
}
143+
144+
async createDownloadStream(
145+
key: string,
146+
options: DownloadOptions,
147+
): Promise<any> {
148+
const decodedKey = decodeURIComponent(trimKey(key))
149+
150+
return new Promise((resolve, reject) => {
151+
this.s3
152+
.headObject({
153+
Bucket: this.config.bucket,
154+
Key: decodedKey,
155+
})
156+
.promise()
157+
.then(() => {
158+
resolve(
159+
this.s3
160+
.getObject({
161+
Bucket: this.config.bucket,
162+
Key: decodedKey,
163+
})
164+
.createReadStream(),
165+
)
166+
})
167+
.catch(e => {
168+
if (e.statusCode === 404) {
169+
e.message = 'File Not Found'
170+
}
171+
throw new Error(`Could not retrieve file from S3: ${e.message}`)
172+
})
173+
})
174+
}
175+
}
176+
177+
export function create(options: S3Config) {
178+
return new S3Wrapper(options)
179+
}

0 commit comments

Comments
 (0)