附件字节
一个简单,有意见的软件包,可以将您 Lucid 模型上的任何一栏转换为附件数据类型.
附件列表允许您在数据库中存储用户上传文件的引用 。 它不需要任何额外的数据库表格,并将文件元数据作为JSON存储在同一列.
怎么样?
这个 attachment-lite 软件包是 媒体图书馆办法的替代办法我相信媒体库在创建CMS时是很棒的.
然而,许多应用如SAAS产品或社区论坛不需要媒体图书馆.
例如,Twitter或dev.to等网站没有媒体库部分,用于上传和选择图片. 相反,这些平台上的图像与资源紧密结合.
当你在Twitter上更新你的配置图像时,旧的图像会消失,而新的图像会出现. 没有图像的中央画廊可以选择配置图片.
一个很长的故事告诉你 attachment-lite 软件包是管理应用程序中一次性文件上传的绝佳解决方案.
特征
- 将数据库中的任何一栏转换为附件数据类型.
- 不需要额外的数据库表格。 文件元数据作为JSON存储在同一列内.
- 当指定新文件时,将自动从磁盘上删除旧文件.
- 优雅地处理失败案件。 如果模型无法持续, 将不存储文件 .
- 同样,如果模型在更新过程中无法持续或删除失败,则不会删除旧文件.
先决条件
这个 attachment-lite 需要软件包 @adonisjs/lucid >= v16.3.1 财务报告和审定财务报表 @adonisjs/core >= 5.3.4。 。 。 .
而且,它依赖于 AdonisJS 驱动器 用于在磁盘上写入文件.
设置
从 npm 登记册中安装软件包如下 .
npm i @adonisjs/attachment-lite
接下来,通过运行以下 A 命令来配置软件包.
node ace configure @adonisjs/attachment-lite
使用量
图像元数据大小往往会超过允许的 SQL 长度 String 数据类型。 因此,建议创建/修改将保留元数据的列,以便使用 JSON 数据类型.
如果您是第一次创建列, 请务必使用 JSON 数据类型 。 示例
// Within the migration file
protected tableName = 'users'
public async up() {
this.schema.createTable(this.tableName, (table) => {
table.increments()
table.json('avatar') // <-- Use a JSON data type
})
}
如果您已经拥有存储图像路径/ URL 的列, 您需要创建新的迁移并更改列定义为 JSON 数据类型 。 示例
# Create a new migration file
node ace make:migration change_avatar_column_to_json --table=users
// Within the migration file
protected tableName = 'users'
public async up() {
this.schema.alterTable(this.tableName, (table) => {
table.json('avatar').alter() // <-- Alter the column definition
})
}
接下来,在模型中导入 attachment 装饰者和 AttachmentContract 界面.
确定不要使用
@column使用时的装饰符@attachment装饰师.
import { BaseModel } from '@ioc:Adonis/Lucid/Orm'
import {
attachment,
AttachmentContract
} from '@ioc:Adonis/Addons/AttachmentLite'
class User extends BaseModel {
@attachment()
public avatar: AttachmentContract
}
现在可以从用户上传文件创建附件如下 .
import { Attachment } from '@ioc:Adonis/Addons/AttachmentLite'
class UsersController {
public store({ request }: HttpContextContract) {
const avatar = request.file('avatar')!
const user = new User()
user.avatar = Attachment.fromFile(avatar)
await user.save()
}
}
这个 Attachment.fromFile 从用户上传文件创建附件类实例 。 当将模型坚持到数据库时,附件-lite会将文件写入磁盘.
处理更新
您可以用新上传的用户文件来更新属性, 包将注意去掉旧文件并存储新文件.
import { Attachment } from '@ioc:Adonis/Addons/AttachmentLite'
class UsersController {
public update({ request }: HttpContextContract) {
const user = await User.firstOrFail()
const avatar = request.file('avatar')!
user.avatar = Attachment.fromFile(avatar)
// Old file will be removed from the disk as well.
await user.save()
}
}
同样,指定 null 要删除文件,而不指定新文件的模型属性值.
另外,确保您更新模型上的属性类型 null 还有.
class User extends BaseModel {
@attachment()
public avatar: AttachmentContract | null
}
const user = await User.first()
user.avatar = null
// Removes the file from the disk
await user.save()
处理删除
在删除模型实例后,所有相关附件都会从磁盘中被移除.
注意: 要删除文件, 需要使用附件列表
modelInstance.delete方法。 使用delete。查询构建器将无法工作.
const user = await User.first()
// Removes any attachments related to this user
await user.delete()
指定磁盘
默认情况下,所有文件都会从默认磁盘中写入/删除. 然而,您可以在使用自定义磁盘时指定 attachment 装饰师.
这个
disk属性价值从未持续到数据库。 这意味着,如果你首先定义磁盘为s3,上传一些文件,然后将磁盘值更改为gcs,软件包将使用gcs磁盘.
class User extends BaseModel {
@attachment({ disk: 's3' })
public avatar: AttachmentContract
}
指定子文件夹
您也可以通过定义 folder 财产如下.
class User extends BaseModel {
@attachment({ folder: 'avatars' })
public avatar: AttachmentContract
}
正在生成 URL
您可以使用该附件生成给定附件的 URL getUrl 或者说 getSignedUrl 方法。 。 。 它们与 驱动方法,只是不需要指定文件名.
await user.avatar.getSignedUrl({ expiresIn: '30mins' })
生成 API 响应的 URL
Drive API生成URL的方法是同步的,而将一个模型序列化到JSON则是同步的. 因此,不是在序列化一个模型时创建URL.
// ❌ Does not work
const users = await User.all()
users.map((user) => {
user.avatar.url = await user.avatar.getSignedUrl()
return user
})
要处理此使用大小写, 您可以选择预计算 URL
预计算 URL
启用 preComputeUrl 在 SELECT 查询后预先计算 URL的旗帜。 例如:
class User extends BaseModel {
@attachment({ preComputeUrl: true })
public avatar: AttachmentContract
}
获取结果
const users = await User.all()
users[0].avatar.url // pre computed already
查找结果
const user = await User.findOrFail(1)
user.avatar.url // pre computed already
接地结果
const users = await User.query.paginate(1)
users[0].avatar.url // pre computed already
这个 preComputeUrl 属性将生成 URL并设置在附件类实例上。 另外,当磁盘是 私营,当磁盘是 公开。 。 。 .
按要求预先计算
我们建议不要允许 preComputeUrl 选项,当您只需要一个或两个查询而不能在应用程序的其余部分使用URL时.
对于这些查询,您可以手动计算控制器内的URL. 这里有一个小的帮助方法,你可以直接在模型上放入.
class User extends BaseModel {
public static async preComputeUrls(models: User | User[]) {
if (Array.isArray(models)) {
await Promise.all(models.map((model) => this.preComputeUrls(model)))
return
}
await models.avatar?.computeUrl()
await models.coverImage?.computeUrl()
}
}
现用如下.
const users = await User.all()
await User.preComputeUrls(users)
return users
或者一个用户
const user = await User.findOrFail(1)
await User.preComputeUrls(user)
return user
与模范工厂使用附件升
附件 lite 首选使用多部分请求机构来坚持用户上传文件. 然而,你也可以构建一个实例: Attachment 手动分类并使用 AdonisJS 驱动器来粘住相应的文件.
在下面的例子中,我们创建了一个附件类的例子来代表邮报封面图像.
import Post from 'App/Models/Post'
import Drive from '@ioc:Adonis/Core/Drive'
import { file } from '@ioc:Adonis/Core/Helpers'
import Factory from '@ioc:Adonis/Lucid/Factory'
import { Attachment } from '@ioc:Adonis/Addons/AttachmentLite'
export default Factory.define(Post, async ({ faker }) => {
/**
* Step 1: Create an instance of attachment
*/
const coverImage = new Attachment({
extname: 'png',
mimeType: 'image/png',
size: 10 * 1000,
name: `${faker.random.alphaNumeric(10)}.png`,
})
/**
* Step 2: Mark image as persisted, this will disable the
* functions of attachment lite that looks for multipart
* body and attempts to write the file from the stream
*/
coverImage.isPersisted = true
/**
* Step 3: Persist the file using Drive.
*/
await Drive.put(coverImage.name, (await file.generatePng('1mb')).contents)
return {
title: faker.lorem.words(5),
coverImage: coverImage,
}
}).build()