diff --git a/package.json b/package.json index 6adb55c..04941cc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kevisual/oss", - "version": "0.0.1", + "version": "0.0.5", "description": "", "main": "dist/index.js", "scripts": { @@ -16,13 +16,23 @@ "license": "MIT", "type": "module", "devDependencies": { - "dotenv": "^16.4.7", + "dotenv": "^16.5.0", "minio": "^8.0.5", "tsup": "^8.4.0" }, "exports": { - "./*": "./dist/*.js", - "./services": "./dist/services/index.js" + ".": { + "import": "./dist/index.js", + "types": "./dist/index.d.ts" + }, + "./config": { + "import": "./dist/services/config.js", + "types": "./dist/services/config.d.ts" + }, + "./services": { + "import": "./dist/services/index.js", + "types": "./dist/services/index.d.ts" + } }, "publishConfig": { "access": "public" diff --git a/src/core/copy-object.ts b/src/core/copy-object.ts index bd397b6..7c99377 100644 --- a/src/core/copy-object.ts +++ b/src/core/copy-object.ts @@ -3,7 +3,7 @@ import { Client, CopyDestinationOptions, CopySourceOptions } from 'minio'; type CopyObjectOpts = { bucketName: string; newMetadata: Record; - prefix: string; + objectName: string; client: Client; }; /** @@ -11,12 +11,14 @@ type CopyObjectOpts = { * @param param0 * @returns */ -export const copyObject = async ({ bucketName, newMetadata, prefix, client }: CopyObjectOpts) => { - const source = new CopySourceOptions({ Bucket: bucketName, Object: prefix }); +export const copyObject = async ({ bucketName, newMetadata, objectName, client }: CopyObjectOpts) => { + const source = new CopySourceOptions({ Bucket: bucketName, Object: objectName }); + const stat = await client.statObject(bucketName, objectName); + const sourceMetadata = stat.metaData; const destination = new CopyDestinationOptions({ Bucket: bucketName, - Object: prefix, - UserMetadata: newMetadata, + Object: objectName, + UserMetadata: { ...sourceMetadata, ...newMetadata }, MetadataDirective: 'REPLACE', }); const copyResult = await client.copyObject(source, destination); diff --git a/src/index.ts b/src/index.ts index b562cb8..9eaf6df 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,12 +1,13 @@ import { Client, ItemBucketMetadata } from 'minio'; import { ListFileObject, ListObjectResult, OssBaseOperation } from './core/type.ts'; import { hash } from './util/hash.ts'; +import { copyObject } from './core/copy-object.ts'; export type OssBaseOptions = { /** * 已经初始化好的minio client */ - client?: Client; + client: Client; /** * 桶名 */ @@ -31,7 +32,7 @@ export class OssBase implements OssBaseOperation { } this.bucketName = opts.bucketName; this.client = opts.client; - this.prefix = opts.prefix; + this.prefix = opts?.prefix ?? ''; } setPrefix(prefix: string) { @@ -64,15 +65,36 @@ export class OssBase implements OssBaseOperation { }); }); } - - async putObject(objectName: string, data: Buffer | string | Object, metaData?: ItemBucketMetadata) { + /** + * 上传文件, + * @param objectName + * @param data + * @param metaData + * @param options 如果文件本身存在,则复制原有的meta的内容 + * @returns + */ + async putObject(objectName: string, data: Buffer | string | Object, metaData: ItemBucketMetadata = {}, options?: { check?: boolean }) { let putData: Buffer | string; + let size: number; if (typeof data === 'object') { putData = JSON.stringify(data); + size = putData.length; + } else if (typeof data === 'string') { + putData = data; + size = putData.length; } else { putData = data; } - const size = putData.length; + if (options?.check) { + const obj = await this.statObject(objectName, true); + if (obj) { + metaData = { + ...obj.metaData, + ...metaData, + }; + } + } + const bucketName = this.bucketName; const obj = await this.client.putObject(bucketName, `${this.prefix}${objectName}`, putData, size, metaData); return obj; @@ -117,10 +139,13 @@ export class OssBase implements OssBaseOperation { return obj as any; } - async statObject(objectName: string) { + async statObject(objectName: string, checkFile = true) { const bucketName = this.bucketName; try { const obj = await this.client.statObject(bucketName, `${this.prefix}${objectName}`); + if (obj && checkFile && obj.size === 0) { + return null; + } return obj; } catch (e) { if (e.code === 'NotFound') { @@ -129,13 +154,29 @@ export class OssBase implements OssBaseOperation { throw e; } } + /** + * 检查文件hash是否一致 + * @param objectName + * @param hash + * @returns + */ + async checkObjectHash(objectName: string, hash: string) { + const obj = await this.statObject(`${this.prefix}${objectName}`, true); + if (!obj) { + return false; + } + return obj.etag === hash; + } async copyObject(sourceObject: any, targetObject: any) { const bucketName = this.bucketName; const obj = await this.client.copyObject(bucketName, sourceObject, targetObject); return obj; } - + async replaceObject(objectName: string, meta: { [key: string]: string }) { + const { bucketName, client } = this; + return copyObject({ bucketName, client, objectName: `${this.prefix}${objectName}`, newMetadata: meta }); + } static create(this: new (opts: OssBaseOptions) => T, opts: OssBaseOptions): T { return new this(opts); } diff --git a/src/services/index.ts b/src/services/index.ts index 8354a2a..ec6cbba 100644 --- a/src/services/index.ts +++ b/src/services/index.ts @@ -1,2 +1,9 @@ -export { OssBase, OssBaseOptions } from '../index.ts'; +export { OssBase } from '../index.ts'; +export type { OssBaseOptions } from '../index.ts'; export { ConfigOssService } from './config.ts'; + +export * from '../util/download.ts'; + +export * from '../util/index.ts'; + +export * from '../core/type.ts'; diff --git a/src/test/config-admin.ts b/src/test/config-admin.ts index 4e93c08..f810b37 100644 --- a/src/test/config-admin.ts +++ b/src/test/config-admin.ts @@ -4,9 +4,16 @@ import { Client } from 'minio'; import path from 'path'; import { downloadObject } from '../util/download.ts'; const cwd = process.cwd(); -dotenv.config({ path: path.resolve(cwd, '..', '..', '.env') }); +dotenv.config({ path: path.resolve(cwd, '..', '..', '.env.dev') }); -console.log(process.env.MINIO_ENDPOINT, process.env.MINIO_USE_SSL, process.env.MINIO_ACCESS_KEY, process.env.MINIO_SECRET_KEY, process.env.MINIO_BUCKET_NAME); +console.log( + 'config', + process.env.MINIO_ENDPOINT, + process.env.MINIO_USE_SSL, + process.env.MINIO_ACCESS_KEY, + process.env.MINIO_SECRET_KEY, + process.env.MINIO_BUCKET_NAME, +); const client = new Client({ endPoint: process.env.MINIO_ENDPOINT, useSSL: process.env.MINIO_USE_SSL === 'true', @@ -25,7 +32,7 @@ const main = async () => { console.log(config); }; -// main(); +main(); const putJson = async () => { const config = await configOssService.putObject( 'a.json', @@ -50,7 +57,7 @@ const downloadMain = async () => { }); // console.log(objectStream); }; -downloadMain(); +// downloadMain(); const statInfo = async () => { try { diff --git a/src/util/download.ts b/src/util/download.ts index 82dd36e..174973c 100644 --- a/src/util/download.ts +++ b/src/util/download.ts @@ -1,7 +1,7 @@ -import { ServerResponse } from 'http'; +import { ServerResponse } from 'node:http'; import { BucketItemStat } from 'minio'; -import fs from 'fs'; -import path from 'path'; +import fs from 'node:fs'; +import path from 'node:path'; const viewableExtensions = ['jpg', 'jpeg', 'png', 'gif', 'svg', 'webp', 'mp4', 'webm', 'mp3', 'wav', 'ogg', 'pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx']; import { OssBase } from '../index.ts'; diff --git a/src/util/hash.ts b/src/util/hash.ts index 321db88..55c8c6a 100644 --- a/src/util/hash.ts +++ b/src/util/hash.ts @@ -1,4 +1,4 @@ -import crypto from 'crypto'; +import crypto from 'node:crypto'; // 582af9ef5cdc53d6628f45cb842f874a // const hashStr = '{"a":"a"}'; // const hash = crypto.createHash('md5').update(hashStr).digest('hex'); diff --git a/tsup.config.ts b/tsup.config.ts index fed798e..9797437 100644 --- a/tsup.config.ts +++ b/tsup.config.ts @@ -4,7 +4,7 @@ const services = glob.sync('src/services/*.ts'); export default defineConfig({ entry: ['src/index.ts', ...services], - + target: 'node22', splitting: false, sourcemap: false, clean: true,