11import { isNullOrEmptyString } from "../utils";
2- import { RequestResponse, ResponseError } from "umi-request";
2+ import { RequestResponse } from "umi-request";
33import { AssumedCredentials, MinioConfig } from ".";
44import { ExtendedRequestMethod, ExtendedRequestOptionsInit } from "../request";
55import { ApplicationError, BucketPolicy } from "../core";
6- import * as Minio from "minio";
7- import { PresignedResult, UploadResult } from "./types";
6+ import { PresignedUrl } from "./types";
87
98export interface MinioOptions {
109 configURL: string;
1110 stsTokenURL: string;
12- genUrl: string;
11+ presignReadUrl: string;
12+ presignUploadURL: string;
1313}
1414
1515interface MinioContext {
@@ -20,10 +20,6 @@ interface MinioContext {
2020
2121const minioContext: MinioContext = { tokenTime: Date.now().valueOf() };
2222
23- type SimpleStringValue = {
24- value: string;
25- };
26-
2723export class MinioUtils {
2824 private constructor(private request: ExtendedRequestMethod, public options: MinioOptions) {}
2925
@@ -33,7 +29,8 @@ export class MinioUtils {
3329 const options: MinioOptions = {
3430 configURL: `${base}/objects/cnf`,
3531 stsTokenURL: `${base}/objects/assume-role`,
36- genUrl: `${base}/objects/pre-sign-url`
32+ presignReadUrl: `${base}/objects/pre-sign-url`,
33+ presignUploadURL: `${base}/objects/pre-sign-upload`
3734 };
3835
3936 return new MinioUtils(request, options);
@@ -91,7 +88,7 @@ export class MinioUtils {
9188 return { data: minioContext, response: { ok: true } } as any;
9289 }
9390
94- public generateObjectUrl(key: string): string | undefined {
91+ public generateObjectUrl(key: string): string {
9592 const config = minioContext.config;
9693 if (config && !isNullOrEmptyString(config.publicBucket)) {
9794 const file = key.startsWith("/") ? key.substr(1, key.length - 1) : key;
@@ -101,71 +98,92 @@ export class MinioUtils {
10198 return `${config.schema}://${config.host}/${config.publicBucket}/${file}`;
10299 }
103100 }
101+ return "";
104102 }
105103
106104 public async presignedObjectUrl(
107- filePath : string,
105+ key : string,
108106 resOptions?: Omit<ExtendedRequestOptionsInit, "method" | "data">
109- ): Promise<RequestResponse<PresignedResult & ApplicationError>> {
110- const file = encodeURI(filePath );
107+ ): Promise<RequestResponse<PresignedUrl & ApplicationError>> {
108+ const file = encodeURI(key );
111109 const requestOptions = { ...(resOptions || {}), method: "POST" };
112- const { response, data } = await this.request<SimpleStringValue & ApplicationError>(
113- `${this.options.genUrl }?key=${file}`,
110+ return await this.request<PresignedUrl & ApplicationError>(
111+ `${this.options.presignReadUrl }?key=${file}`,
114112 requestOptions
115113 );
116- return { data: { url: (data?.value || "") }, response }
117114 }
118115
119-
120- private putObjectAsync(
121- client: Minio.Client,
122- bucketName: string,
123- objectName: string,
124- stream: any
125- ): Promise<RequestResponse<UploadResult>> {
126- return new Promise((resolve, reject) => {
127- client.putObject(bucketName, objectName, stream, (err, etag) => {
128- if (err === undefined || err === null) {
129- resolve({ data: { etag }, response: { ok: true, status: 200, type: "default" } as any });
130- } else {
131- const data: UploadResult = { etag };
132- if (typeof err === "string") {
133- data.error = err as any;
134- data.error_description = err as any;
135- } else {
136- data.error = err?.name;
137- data.error_description = err?.message;
138- }
139-
140- reject({ data, response: { ok: false, status: 500 } as any });
141- }
142- });
143- });
116+ public async presignedUploadUrl(
117+ key: string,
118+ bucket: BucketPolicy = BucketPolicy.Public,
119+ resOptions?: Omit<ExtendedRequestOptionsInit, "method" | "data">
120+ ): Promise<RequestResponse<PresignedUrl & ApplicationError>> {
121+ const r = await this.getMinioContext();
122+ if (!r.response.ok) {
123+ return r as any;
124+ }
125+ const file = encodeURI(key);
126+ const requestOptions = { ...(resOptions || {}), method: "POST" };
127+ return await this.request<PresignedUrl & ApplicationError>(
128+ `${this.options.presignUploadURL}?key=${file}&b=${bucket}`,
129+ requestOptions
130+ );
144131 }
145132
146- public async upload(bucketPolicy: BucketPolicy, key: string, stream: any): Promise<RequestResponse<UploadResult>> {
147- const r = await this.getMinioContext();
133+ private getFileName(key: string): string {
134+ const correctKey = key.replace("\\", "/");
135+ const index = correctKey.lastIndexOf("/");
136+ return correctKey.substr(index);
137+ }
148138
149- const { response, data } = r;
150- if (!r.response.ok) {
151- return { data: data as any, response };
139+ public async upload(
140+ key: string,
141+ blobOrString: Blob | string,
142+ bucketPolicy: BucketPolicy = BucketPolicy.Public
143+ ): Promise<RequestResponse<PresignedUrl & ApplicationError>> {
144+ const fileAPI = window.File && window.FileReader && window.FileList && window.Blob;
145+ if (!fileAPI) {
146+ return {
147+ response: { ok: false, status: 0, statusText: "file api unsupported" } as any,
148+ data: {
149+ error: "file_api_unsupported",
150+ error_description: "file api unsupported by current broswser."
151+ } as any
152+ };
152153 }
153154
154- const { stsToken, config } = r.data;
155- const cnf = config as MinioConfig;
155+ const r = await this.getMinioContext();
156+ const fileName = this.getFileName(key);
157+ const length = typeof blobOrString === "string" ? blobOrString.length : blobOrString.size;
158+ const blob = typeof blobOrString === "string" ? new Blob([blobOrString], { type: "plain/text" }) : blobOrString;
156159
157- const bucketName = bucketPolicy === BucketPolicy.Private ? cnf.privateBucket : cnf.publicBucket ;
160+ const presignedResult = await this.presignedUploadUrl(key, bucketPolicy) ;
158161
159- const minioClient = new Minio.Client({
160- endPoint: cnf.host,
161- port: cnf.port,
162- region: cnf.region,
163- useSSL: cnf.schema === "https",
164- accessKey: stsToken?.accessKey || "",
165- secretKey: stsToken?.secretKey || "",
166- sessionToken:stsToken?.sessionToken || "",
167- });
162+ if (presignedResult.response.ok) {
163+ const formData = new FormData();
164+ formData.append("file", blob, fileName);
168165
169- return await this.putObjectAsync(minioClient, bucketName, key, stream);
166+ const { url, contentType } = presignedResult.data;
167+ const { response, data } = await this.request.put(url, {
168+ data: formData,
169+ headers: { "Content-Length": `${length}`, "Content-Type":"multipart/form-data" }
170+ });
171+
172+ if (response.ok) {
173+ if (bucketPolicy === BucketPolicy.Private) {
174+ const presigned = await this.presignedObjectUrl(key);
175+ if (presigned.response.ok) {
176+ return presigned;
177+ }
178+ }
179+ return {
180+ response: { ok: true, status: 200 },
181+ data: { url: this.generateObjectUrl(key), expireInSeconds: Number.MAX_VALUE, contentType }
182+ } as any;
183+ }
184+ return { response, data } as any;
185+ } else {
186+ return presignedResult as any;
187+ }
170188 }
171189}
0 commit comments