跳到主要内容

对象存储 SDK

ONES 提供插件存储能力

ONES 的实体存储 SDK 允许你使用各种查询条件查询存储在这些结构中的数据,或者对数据进行创建、更新、删除等数据管理操作。

目前支持的存储能力 SDK 有:

要求

ONES@ones-op/sdk@ones/cli
v6.36.0v1.50.0v1.50.0

对象存储 SDK

上传一个对象

上传一个对象,需要两步:

  • 第一步:使用 SDK 获取上传地址和参数。
  • 第二步:使用上传地址和参数(包括目标文件)上传对象。

上传预签

示例用法

backend/src/index.ts
import { storage } from '@ones-op/sdk/backend'
import type { ObjectStoreUploadResult } from '@ones-op/sdk/backend'

const { object } = storage

const result = <ObjectStoreUploadResult>await object.upload('cover')
// 获取上传地址(在插件后端请求)
const url = result.getUrl()
// 获取上传地址(在浏览器中请求)
const webUrl = result.getWebUrl()
// 获取请求的 FormData 参数
const fields = result.getFields()

返回值类型

backend/src/index.ts
type result = {
getUrl(): string // 获取上传地址(在插件后端请求)
getWebUrl(): string // 获取上传地址(在浏览器中请求)
getFields(): ObjectResultDataFields // 获取请求的 FormData 参数
}
type ObjectResultDataFields = Record<string, string>

上传对象(后端)

具体上传方式,由业务实现。

示例用法

  • url,通过 result.getUrl() 获取。
  • data,创建 FormData 的实例。
    • FormData,在 Node.js 中,你需要自己实现 FormData
      • 或者使用社区的第三方库,例如:formdata-node@5.0.1
    • fields,通过 result.getFields() 获取,并且逐一透传。
      • 如果 fields 的 key 缺省,或者修改过它的内容,会导致校验失败。
    • file,上传对象的二进制数据以及文件名。
      • 如果你的文件是 base64 或者 Buffer,都可以转换为 Blob 类型的数据。
  • method,设置为 post
  • headersContent-Type 设置为 multipart/form-data
  • 上传成功的 status201
backend/src/index.ts
import { FormData } from 'formdata-node' // formdata-node@5.0.1
import { storage } from '@ones-op/sdk/backend'
import type { ObjectStoreUploadResult } from '@ones-op/sdk/backend'

const { object } = storage

const result = <ObjectStoreUploadResult>await object.upload('cover')

// case: 'https://example.com/upload/'
const url = result.getUrl()
// case: { Policy: 'policy', ... }
const fields = result.getFields()

const form = new FormData()
Object.keys(fields).forEach((key) => {
const value = fields[key]
form.append(key, value)
})
const blob = new Blob([buffer]) // cover blob data
form.append('file', blob, 'cover')

// axios or fetch or any request methods
axios({
url,
data: form,
method: 'post',
headers: {
'Content-Type': 'multipart/form-data',
},
}).then((response) => {
console.log(response)
if (response.status === 201) {
console.log('uploaded successfully')
}
})

上传对象(前端)

具体上传方式,由业务实现。

示例用法

  • url,通过 result.getWebUrl() 获取。
    • 你需要实现一个 addition 接口。
    • 先调用 SDK 的方法,再返回结果给插件前端。
    • 该地址可能是绝对路径,也可能是相对路径,详情请参考:webUrl 使用说明
  • data,创建 FormData 的实例。
    • fields,通过 result.getFields() 获取,并且逐一透传。
      • 你需要实现一个 addition 接口。
      • 先调用 SDK 的方法,再返回结果给插件前端。
      • 如果 fields 的 key 缺省,或者修改过它的内容,会导致校验失败。
    • file,通过 <input type="file"> 获取。
  • method,设置为 post
  • headersContent-Type 设置为 multipart/form-data
  • 上传成功的 status201
backend/src/index.ts
import { storage } from '@ones-op/sdk/backend'
import type { ObjectStoreUploadResult } from '@ones-op/sdk/backend'

const { object } = storage

export async function hello() {
const result = <ObjectStoreUploadResult>await object.upload('cover')
return {
body: {
url: result.getWebUrl(),
fields: result.getFields(),
}
}
}
web/src/example/index.tsx
import { OPFetch } from '@ones-op/fetch'

const handleClick = async () => {
const response = await OPFetch.post('/project/api/project/hello')

// case1: 'https://example.com/upload/'
// case2: '/upload/'
const url = response.data.data.url
// case: { Policy: 'policy', ... }
const fields = response.data.data.fields

const form = new FormData()
Object.keys(fields).forEach((key) => {
const value = fields[key]
form.append(key, value)
})
const input = document.querySelector('input')
const file = input!.files?.[0] // file from <input type="file">
form.append('file', file!)

// axios or fetch or any request methods
axios({
url,
data: form,
method: 'post',
headers: {
'Content-Type': 'multipart/form-data',
},
}).then((response) => {
console.log(response)
if (response.status === 201) {
console.log('uploaded successfully')
}
})
}

下载一个对象

下载一个对象,需要两步:

  • 第一步:使用 SDK 获取下载地址。
  • 第二步:使用下载地址下载对象。

下载预签

示例用法

backend/src/index.ts
import { storage } from '@ones-op/sdk/backend'
import type { ObjectStoreDownloadResult } from '@ones-op/sdk/backend'

const { object } = storage

const result = <ObjectStoreDownloadResult>await object.download('cover')
// 获取下载地址(在插件后端请求)
const url = result.getUrl()
// 获取下载地址(在浏览器中请求)
const webUrl = result.getWebUrl()

返回值类型

backend/src/index.ts
type result = {
getUrl(): string // 获取下载地址(在插件后端请求)
getWebUrl(): string // 获取下载地址(在浏览器中请求)
}

下载对象(后端)

具体下载方式,由业务实现。

示例用法

  • url,通过 result.getUrl() 获取。
  • method,设置为 get
backend/src/index.ts
import { storage } from '@ones-op/sdk/backend'
import type { ObjectStoreDownloadResult } from '@ones-op/sdk/backend'

const { object } = storage

const result = <ObjectStoreDownloadResult>await object.download('cover')

// case: 'https://example.com/download/'
const url = result.getUrl()

// axios or fetch or any request methods
axios({
url,
method: 'get',
}).then((response) => {
console.log(response)
})

下载对象(前端)

具体下载方式,由业务实现。

  • 下载的文件的文件名,可以使用 <a> 标签的 download 属性自定义。

示例用法

  • url,通过 result.getWebUrl() 获取。
    • 你需要实现一个 addition 接口。
    • 先调用 SDK 的方法,再返回结果给插件前端。
    • 该地址可能是绝对路径,也可能是相对路径,详情请参考:webUrl 使用说明
backend/src/index.ts
import { storage } from '@ones-op/sdk/backend'
import type { ObjectStoreDownloadResult } from '@ones-op/sdk/backend'

const { object } = storage

export async function hello() {
const result = <ObjectStoreDownloadResult>await object.download('cover')
return {
body: {
url: result.getWebUrl(),
}
}
}
web/src/example/index.tsx
import { OPFetch } from '@ones-op/fetch'

const handleClick = async () => {
const response = await OPFetch.post('/project/api/project/hello')

// case1: 'https://example.com/download/'
// case2: '/download/'
const url = response.data.data.url

const a = document.createElement('a')
a.href = url
a.setAttribute('download', 'cover')
a.click()
}

预览图片或者文件(前端)

具体预览方式,由业务实现。

  • 浏览器不支持预览的文件类型,需要由业务实现预览逻辑。
  • 如果在浏览器中打开文件的地址,默认是下载行为。

示例用法

  • url,通过 result.getWebUrl() 获取。
    • 你需要实现一个 addition 接口。
    • 先调用 SDK 的方法,再返回结果给插件前端。
    • 该地址可能是绝对路径,也可能是相对路径,详情请参考:webUrl 使用说明
backend/src/index.ts
import { storage } from '@ones-op/sdk/backend'
import type { ObjectStoreDownloadResult } from '@ones-op/sdk/backend'

const { object } = storage

export async function hello() {
const result = <ObjectStoreDownloadResult>await object.download('cover')
return {
body: {
url: result.getWebUrl(),
}
}
}
web/src/example/index.tsx
import { OPFetch } from '@ones-op/fetch'

const response = await OPFetch.post('/project/api/project/hello')

// case1: 'https://example.com/download/'
// case2: '/download/'
const url = response.data.data.url

const preview = <img src={url}></img>

删除一个对象

示例用法

backend/src/index.ts
import { storage } from '@ones-op/sdk/backend'

const { object } = storage

object.delete('cover')

获取一个对象的元信息

示例用法

backend/src/index.ts
import { storage } from '@ones-op/sdk/backend'
import type { ObjectMetadataResultData } from '@ones-op/sdk/backend'

const { object } = storage

const data = <ObjectMetadataResultData>await object.metadata('cover')
// 对象存储 key
console.log(data.object_key)
// 文件大小,字节
console.log(data.size)

返回值类型

backend/src/index.ts
type data = {
object_key: string // 对象存储 key
size: number // 文件大小,字节
}

SDK 使用规范

预签名有效期

预签名地址有效期为 1 小时。过期后需要重新签名。

从调用 SDK 后开始计时。

webUrl 使用说明

根据 ONES 实例的部署情况,webUrl 可能是绝对路径,也可能是相对路径

如果你是在浏览器中,上传对象或者下载对象,浏览器会根据当前 ONES 的 host 自动填充。

web/src/example/index.tsx
// case1: absolute url
axios({
url: 'https://example.com/upload/',
data: form,
method: 'post',
headers: {
'Content-Type': 'multipart/form-data',
},
})

// case2: relative url
// case2 is the same as case1 when host is https://example.com
axios({
url: '/upload/',
data: form,
method: 'post',
headers: {
'Content-Type': 'multipart/form-data',
},
})

如果你是在脚本中,上传对象或者下载对象,则需要手动填充 host

let url = result.getWebUrl()
if (/^\//.test(url)) {
const host = 'https://example.com'
url = `${host}${url}`
}

SDK 错误处理

对象操作均为异步,以下方法的错误处理,均可参照示例用法:

  • upload
  • download
  • delete
  • metadata

示例用法

backend/src/index.ts
import { storage } from '@ones-op/sdk/backend'
import type { ObjectError, ObjectStoreUploadResult } from '@ones-op/sdk/backend'

const { object } = storage

// 捕抓错误的写法
async function hello() {
const result = await object.upload('cover')
if (result instanceof object.ObjectError) {
throw result
}
return {
url: result.getWebUrl(),
fields: result.getFields(),
}
}

// 不捕抓错误的写法
async function hello() {
const result = <ObjectStoreUploadResult>await object.upload('cover')
return {
url: result.getWebUrl(),
fields: result.getFields(),
}
}

返回值类型

backend/src/index.ts
// 错误对象
type ObjectError = {
code: string
err_msg: string
}

SDK 常见错误

错误码描述
ObjectKeyInvalid对象存储 key 为空/包含非法字符/超出最大长度限制
ObjectKeyNotfound对象存储 key 不存在
RequestTimeOut请求超时

如果在上传对象或者下载对象时,出现错误,则返回 S3 标准错误码,具体请参考:S3 错误码