UNPKG

@shencom/api

Version:
423 lines (356 loc) 12.9 kB
import type { Dictionary, Unfurl } from '@shencom/typing'; import { getInitializedApiConfig } from '../config'; import type { RequestConfig } from '../type'; function isFormData(val: any): val is FormData { return Object.prototype.toString.call(val) === '[object FormData]'; } interface ManagementSingOptions { /** `0:私有`,`1:开放`,`默认: 1` */ open?: 0 | 1; /** 上传到的位置 `0: aliyunoss`,`1: minio`,`默认: 0` */ target?: 0 | 1; } interface ManagementUploadOptions extends ManagementSingOptions { /** 音频文件时长,单位:秒 */ fileLength?: number; // 临时链接的过期时间,单位:秒。当open为false时传才有效。注意:慎用阿里云的过期时间,ServerTime 可能比本地块,待调整。 expiry?: number; } type Ids = string | string[]; type FileInfo = File | { path: string; size: number }; /** 文件详情 */ export const ApiFileShow = (body: { ids: Ids }, headers?: Record<string, any>) => { const { url, http } = getInitializedApiConfig(); const api = `${url}/service-file/file/show`; return http.post<SC.File.Info[]>(api, body, { headers }); }; /** * 获取oss直传所需Policy和签名 * @param {string} open `0:私有`,`1:开放`,`默认: 1` * @param {string} target 上传到的位置 上传到的位置 `0: aliyunoss`,`1: minio`,`默认: 0` */ export const ApiFileOssSign = ( body: Unfurl<ManagementSingOptions> = {}, headers?: Record<string, any>, ) => { const { url, http } = getInitializedApiConfig(); const api = `${url}/service-file/file/management/sign`; return http.get<SC.OSS.Sign>(api, { target: 0, open: 1, ...body }, { headers }); }; /** 和 sign 的区别是上传指定的文件 */ type ReqFileOssSignV2 = Unfurl< ManagementSingOptions & { /** 文件名, 后端主要用于校验文件类型等 */ name: string; } >; /** * 获取oss直传所需Policy和签名 V2 * @param {string} open `0:私有`,`1:开放`,`默认: 1` * @param {string} target 上传到的位置 上传到的位置 `0: aliyunoss`,`1: minio`,`默认: 0` * @param {string} name 文件名,需要带上文件后缀,eg:`test.png` */ export const ApiFileOssSignV2 = (body: ReqFileOssSignV2, headers?: Record<string, any>) => { const { url, http } = getInitializedApiConfig(); const api = `${url}/service-file/file/management/signV2`; return http.post<SC.OSS.SignV2>(api, { target: 0, open: 1, ...body }, { headers }); }; type ReqFileOssBatchSignV2 = Unfurl<ManagementSingOptions & { names: string[] }>; /** * 批量获取oss直传所需Policy和签名 V2 * @param {string} open `0:私有`,`1:开放`,`默认: 1` * @param {string} target 上传到的位置 上传到的位置 `0: aliyunoss`,`1: minio`,`默认: 0` * @param {string[]} names 文件名,需要带上文件后缀,eg:`["test.png", "211.jpeg"]` */ export const ApiFileOssBatchSignV2 = ( body: ReqFileOssBatchSignV2, headers?: Record<string, any>, ) => { const { url, http } = getInitializedApiConfig(); const api = `${url}/service-file/file/management/batchSignV2`; return http.post<SC.OSS.SignV2[]>(api, { target: 0, open: 1, ...body }, { headers }); }; function HandleFileETag(headers: Dictionary<any> = {}) { let etag = headers.etag || headers.ETag || headers.Etag; if (!etag) return; if (Array.isArray(etag)) etag = etag[0]; try { etag = JSON.parse(etag) as string; } catch (error) { // } return etag; } interface ReqFileOssUpload extends SC.OSS.Sign { /** 文件随机名称 */ fileName: string; /** 文件 */ file: FileInfo; } type ResFileOssUpload = Omit<SC.File.Info, 'createdAt' | 'remark' | 'id' | 'memo'>; /** * oss签名上传文件 * @param {object} file 文件对象 * @param {object} fileName 文件随机名 * @param {string} accessid OSS 的 `AccessKeyId` * @param {string} host 上传存储空间的访问域名 * @param {string} signature 签名信息 * @param {string} policy 上传所需policy信息 * @param {string} dir 用户上传文件时指定的前缀 * @link https://help.aliyun.com/document_detail/31988.htm?spm=a2c4g.31925.0.0.67fe5918MHHN8l#reference-smp-nsw-wdb */ export const ApiFileOssUpload = async (body: Unfurl<ReqFileOssUpload>, custom?: RequestConfig) => { const { http } = getInitializedApiConfig(); const { file, fileName, accessid, host, signature, policy, dir } = body; const key = `${dir}${fileName}`; let name; if ('name' in file) name = file.name; // 文件真实名称 const param: Dictionary = { key, file, policy, signature, OSSAccessKeyId: accessid, success_action_status: '200', // 下载时的文件名 'Content-Disposition': name ? `attachment;filename="${encodeURIComponent(name)}"` : undefined, }; let formData: FormData | Dictionary; if (typeof FormData === 'undefined') { formData = param; } else { formData = new FormData(); const formDataKey = Object.keys(param) as (keyof typeof param)[]; formDataKey.forEach((key) => { if (key === 'file') return; if (param[key] === undefined || param[key] === null) return; formData.append(key as string, `${param[key]}`); }); formData.append('file', param.file as Blob); // 必须放在最后一个 } const res = await http.upload<any, typeof formData, true>(host, formData, custom); const etag = HandleFileETag(res.headers); const data = { sign: 1, etag, fileName, signature, fileSize: file.size, name: name || fileName, remoteUrl: `${host.replace(/(?=\/)\/$/, '')}/${key}`, // 处理 host 可能后面添加 / 的情况 }; return data as ResFileOssUpload; }; interface ReqFileOssUploadV2 extends SC.OSS.SignV2 { /** 文件随机名称 */ fileName: string; /** 文件 */ file: FileInfo; } /** * oss签名上传文件 V2 * @param {object} file 文件对象 * @param {object} fileName 文件随机名 * @param {string} accessid OSS 的 `AccessKeyId` * @param {string} host 上传存储空间的访问域名 * @param {string} signature 签名信息 * @param {string} policy 上传所需policy信息 * @param {string} objectName 上传至 oss 的完整文件路径 * @link https://help.aliyun.com/document_detail/31988.htm?spm=a2c4g.31925.0.0.67fe5918MHHN8l#reference-smp-nsw-wdb */ export const ApiFileOssUploadV2 = async ( body: Unfurl<ReqFileOssUploadV2>, custom?: RequestConfig, ) => { const { http } = getInitializedApiConfig(); const { file, fileName, accessid, host, signature, policy, objectName } = body; let name; if ('name' in file) name = file.name; // 文件真实名称 const param: Dictionary = { key: objectName, file, policy, signature, OSSAccessKeyId: accessid, success_action_status: '200', // 下载时的文件名 'Content-Disposition': name ? `attachment;filename="${encodeURIComponent(name)}"` : undefined, }; let formData: FormData | Dictionary; if (typeof FormData === 'undefined') { formData = param; } else { formData = new FormData(); const formDataKey = Object.keys(param) as (keyof typeof param)[]; formDataKey.forEach((key) => { if (key === 'file') return; if (param[key] === undefined || param[key] === null) return; formData.append(key as string, `${param[key]}`); }); formData.append('file', param.file as Blob); // 必须放在最后一个 } const res = await http.upload<any, typeof formData, true>(host, formData, custom); const etag = HandleFileETag(res.headers); const data = { sign: 1, etag, fileName, signature, fileSize: file.size, name: name || fileName, remoteUrl: `${host.replace(/(?=\/)\/$/, '')}/${objectName}`, // 处理 host 可能后面添加 / 的情况 }; return data as ResFileOssUpload; }; type ReqFileUpdate = Pick<SC.File.Info, 'fileName' | 'name' | 'remoteUrl'> & { /** etag字段 */ remark?: string; etag?: string; fileSize?: number; }; /** 将 oss 文件信息更新到数据库 */ export const ApiFileUpdate = (body: Unfurl<ReqFileUpdate>, headers?: Record<string, any>) => { const { http, url } = getInitializedApiConfig(); const api = `${url}/service-file/file/update`; return http.post<SC.File.Info>(api, body, { headers }); }; type ReqFileUpload = ManagementSingOptions & { /** 文件信息 */ file: FileInfo; /** 访问链接是否下载 */ isDownload?: boolean; /** 通过 isDownload 属性控制 */ disposition?: string; }; function HandleFileUploadBody(defaultBody: Dictionary, body: FormData | Unfurl<ReqFileUpload>) { const param = body; // 小程序情况下 if (typeof FormData === 'undefined' && !isFormData(param)) { if (param.isDownload === true) { param.disposition = `attachment;filename=`; } const data = { ...defaultBody, ...param }; delete data.isDownload; return data; } if (!isFormData(param)) { const formData = new FormData(); const data = { ...defaultBody, ...param }; const formDataKey = Object.keys(data) as (keyof typeof data)[]; formDataKey.forEach((key) => { if (key === 'file') return; if (data[key] === undefined || data[key] === null) return; if (key === 'isDownload' && data[key]) { formData.append('disposition', `attachment;filename=`); return; } formData.append(key, `${data[key]}`); }); formData.append('file', data.file as Blob); // 必须放在最后一个 return formData; } else { if (param.has('isDownload')) { param.delete('isDownload'); param.append('disposition', `attachment;filename=`); } Object.keys(defaultBody).forEach((key) => { const value = param.get(key); if (value === undefined) param.append(key, `${defaultBody[key]}`); }); } return param; } export interface ResFileUpload { id: string; etag: string; name: string; fileName: string; fileSize: string; url: string; } /** 服务器上传 */ export const ApiFileUpload = (body: FormData | Unfurl<ReqFileUpload>, custom?: RequestConfig) => { const { http, url } = getInitializedApiConfig(); const api = `${url}/service-file/file/management/upload/single`; const formData = HandleFileUploadBody({ target: 0, open: 1, isDownload: true }, body); return http.upload<ResFileUpload>(api, formData, custom); }; export interface ResStsToken { /** oss accessKeyId */ accessid: string; /** oss accessKeySecret */ accessSecret: string; /** 过期时间,秒 */ expire: number; bucket: string; region: string; securityToken: string; } /** * 获取临时sts-token【全功能】 * * @param target 文件源 aliyunoss: 0, minio: 1, 默认: 0 * @param open 文件权限 开放: 0, 私有文件: 1, 默认: 1 */ export const ApiFileStsToken = ( body?: Unfurl<Partial<ManagementSingOptions & { bucketName?: string }>>, headers?: Record<string, any>, ) => { const { http, url } = getInitializedApiConfig(); const api = `${url}/service-file/file/management/tempCrudStsToken`; return http.post<ResStsToken>(api, { target: 0, open: 1, ...body }, { headers }); }; export interface PresignedResponse { id: string; /** 有效时间 */ expiry: number; /** 文件名 */ name: string; /** 文件链接 */ url: string; } /** * 获取私有临时文件访问链接 * * @param ids id * @param expiry 有效时间单位秒 * @param target 文件源 aliyunoss: 0, minio: 1, 默认: 0 * @param open 文件权限 开放: 0, 私有文件: 1, 默认: 1 */ export const ApiFilePresigned = ( body: Unfurl<Omit<ManagementUploadOptions, 'open'>>, headers?: Record<string, any>, ) => { const { http, url } = getInitializedApiConfig(); const api = `${url}/service-file/file/management/presigned`; return http.post<PresignedResponse[]>( api, { target: 0, ...body, open: 0 }, { headers: { Authorization: null, ...headers } }, ); }; /** * 判断是否重复照片 * @param {string} etag - 文件返回的etag */ export const ApiFileExist = (body: { etag: string }, headers?: Record<string, any>) => { const { http, url } = getInitializedApiConfig(); const api = `${url}/service-file/file/exist`; return http.get<SC.File.Info[]>(`${api}/${body.etag}`, {}, { headers }); }; export interface ReqCheckFile { origin: string; files: { id: string; etag: string }[]; } /** * 判断重复照片 * @param origin - 业务标识符,若不传则当前检测租户下所有文件 * @param files - 重复的文件列表 * @param files.id - 重复的文件id * @param files.etag - 重复的文件etag */ export const ApiFileRepeat = (body: ReqCheckFile, headers?: Record<string, any>) => { const { http, url } = getInitializedApiConfig(); const api = `${url}/service-file/file/management/repeatpic/check`; return http.post<ReqCheckFile>(api, body, { headers }); };