Supabase 存储 CDN 和 Serverless 功能转换(非官方)
本教程只是一个临时替代方案,而 Supabase 团队正在努力提供越来越多的功能,其中存储 CDN 和转换正在他们的管道中。
[
](https://res.cloudinary.com/practicaldev/image/fetch/s--0TwdoLKe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode .com/res/hashnode/image/upload/v1631279067782/63nFl5KeN.png)
来源:https://supabase.io/storage
⭐🎉🎊
关于这一点,祝贺 Supabase 团队作为开源后端即服务初创公司筹集了美元 3000 万美元!
开始吧!
注意⚠:
-
我们将使用 Vercel Serverless 函数来实现这一点,代码可能不同但逻辑相同。
-
我们将只服务和转换
Public桶。如果您想了解如何使用 Supbase Auth for RLS 实现这些魔法,请记得关注我获取更多教程。
话虽如此,我们将通过几个简单的步骤来实现这个魔法
我们的 Supabase Storage 的图像。
1\。获取图像存储桶和名称
我们将使用bucket_name和file_name变量来调用无服务器函数,而不是完整的公共 url。如果不是这样,您的图片链接将超级冗长,而且是不必要的。
以下是您可以准备bucket_name和/或file_name的一些方法。
1.如果您允许您的用户将静态内容上传到Public存储桶,请记下键入的bucket_name和file_name用户。
const bucket_name = 'static' // your bucket name
const file_name = 'avatar.png' // name for the file
const avatarFile = event.target.files[0]
const { data, error } = await supabase
.storage
.from('avatars')
.upload(`${ bucket_name }/${ file_name }`, avatarFile, {
cacheControl: '3600',
upsert: false
})
进入全屏模式 退出全屏模式
2.您可以使用from.list()在bucket中检索您想要的图像。
在这种情况下,我将简单地列出我的bucket_name存储桶中的所有内容。
const { data, error } = await supabase.storage.from(bucket_name).list()
const file_names = data.map(item => item.names)
进入全屏模式 退出全屏模式
3.如果您已经在另一个查询中一起获取公共URL,例如https://asdasaeipbvsvnr.supabase.co/storage/v1/object/public/static/avatar.png,那么您可以使用快速获取bucket_name和file_name
let link = 'https://asdasaeipbvsvnr.supabase.co/storage/v1/object/public/static/avatar.png'
let [ bucket_name, file_name ] = link.split('public/')[1].split('/')
进入全屏模式 退出全屏模式
好的,现在我们有了合适的变量,我们可以开始构建我们的新链接以插入<img>标记! 🙌
2\。构建新链接
因为我们使用的是 Vercel 无服务器功能,所以我们需要将我们的 img url 包裹在api路由周围。
如果您在当前项目中使用 Vercel,您可以简单地使用以下代码为您的<img>生成新链接
const params = new URLSearchParams({
f: file_name,
b: bucket_name,
// params we haven't mentioned...
})
const new_link = window.location.origin + "/api/resize?" + params.toString()
进入全屏模式 退出全屏模式
如果您不使用 Vercel 作为部署,您可以轻松地分叉这个我为本教程创建的 repo。您只需按照步骤在 Vercel 上设置您的.env。如果您想了解有关此功能如何工作的更多信息,请继续关注!
无服务器功能
这部分是神奇的地方,让我们在项目根目录中创建一个新文件,命名为api/resize.ts(默认情况下,Vercel 会将 api 文件夹中的所有文件转换为无服务器函数)。
然后,您必须安装一些软件包
我正在使用 yarn 和 typescript,如果你愿意,你可以使用 npm 和纯 Javascript
yarn add sharp axios
yarn add -D @vercel/node @types/sharp
进入全屏模式 退出全屏模式
接下来,创建一个基本函数,如下所示:
import { VercelRequest, VercelResponse } from "@vercel/node"
import sharp from "sharp"
import axios from "axios"
export default async (req: VercelRequest, res: VercelResponse) => {
res.end("Hi")
}
进入全屏模式 退出全屏模式
要快速测试api,请运行vercel dev以启动 Vercel 开发服务器。
然后访问http://localhost:3000/api/resize,它应该以“Hi”响应。
之后,将函数替换为:
export default async (req: VercelRequest, res: VercelResponse) => {
const {
query: { w, h, f, b, q },
} = req
// this tricks to deconstruct all the nested query into it's own variable.
// parameters
// w: width (pixel)
// h: height (pixel)
// f: file_name
// b: bucket_name
// q: quality (0 to 100)
res.end("Hi")
}
进入全屏模式 退出全屏模式
还记得我们刚刚为图像创建了一个新链接吗?现在我们必须将其构造回原始 url,然后将其转换为 Buffer。值得庆幸的是,axios 让这项工作变得如此简单。
export default async (req: VercelRequest, res: VercelResponse) => {
...
// check if `bucket_name` and `file_name` are available, else return error
if (f && b) {
const url = `${ process.env.SUPABASE_URL }/storage/v1/object/public/${ b }/${ f }`
const buffer = (await axios({ url, responseType: "arraybuffer" })).data as Buffer
res.statusCode = 200
res.setHeader("Content-Type", "image/png")
res.end(buffer)
} else {
res.statusCode = 500
res.setHeader("Content-Type", "text/html")
res.end("<h1>Internal Error</h1><p>Sorry, there was a problem</p>")
}
}
进入全屏模式 退出全屏模式
您现在可以测试此 api 端点为http://localhost:3000/api/resize?f=avatar.png&b=static(当然您需要将图像放在存储桶中)以查看是否生成了图像。如果可行,让我们继续本教程中最长的脚本,我们使用Sharp将图像转换为所需的宽度、高度或质量。
export default async (req: VercelRequest, res: VercelResponse) => {
...
if (f && b) {
...
// here we create a new_params object to convert string to number, and also set default value
const new_params = {
w: +w || 800, // set default 800px
h: +h || null, // set to null if not provided, so that Sharp automatically keep the aspect ratio
q: +q || 80 // set default 80% quality
}
// here's where the Transformation happens
sharp(buffer)
.resize(new_params.w, new_params.h)
.jpeg({quality: new_params.q}) // change to .webp() if you want to serve as webp
.toBuffer()
.then((data) => {
// here's where set the cache
// I set to cache the media for 1 week, 60seconds * 60minutes * 24hours * 7days
// remove setHeader('Cache-Control') if you wish not to cache it
res.statusCode = 200
res.setHeader("Cache-Control", `public, immutable, no-transform, s-maxage=604800, max-age=604800`)
res.setHeader("Content-Type", "image/jpeg")
res.end(data)
})
} else {
res.statusCode = 500
res.setHeader("Content-Type", "text/html")
res.end("<h1>Internal Error</h1><p>Sorry, there was a problem</p>")
}
}
进入全屏模式 退出全屏模式
而已!只需几行代码,您就可以拥有自己的 CDN 和 Supabase 存储转换!!!!但!不要忘记我们在前端创建的new_link。
最后!
这是本教程的最后一步,我们之前生成了new_link,但现在可以添加更多参数了。
// Set a few width so that cache is more efficient, and need not to create so many cache when different browser visit your website.
let windowWidth = 0
if(window.innerWidth >= 1200) {
windowWidth = 1000
} else if (window.innerWidth >= 800) {
windowWidth = 800
} else {
windowWidth = 600
}
const params = new URLSearchParams({
f: file_name,
b: bucket_name,
w: windowWidth,
h: null, // set to null to keep image's aspect ratio
q: 0.8
})
const new_link = window.location.origin + "/api/resize?" + params.toString()
// set the src to new link
document.getElementById("myImg").src = new_link;
进入全屏模式 退出全屏模式
我们完成了!
本教程的所有源代码都可以在这里找到!
展示柜

查看Made With Supabase,并检查<img>,您将在那里看到类似的代码,但有细微的变化。
什么是用 Supabase 制成的?这是 ** 用 Supabase 制作的项目集合**!随意提交您的 Supabase 项目,与世界分享 Supabase 的精彩!
出发前
如果您觉得本教程对您有帮助,并希望进一步学习,请在此处关注我,并关注我的 Twitter!
泽尔尼亚
@zernonia
如果您的项目使用@supabase,请记住#madewithsupabase或在@madewifsupabase网站上提交您的项目哟
15:32 PM - 2021 年 8 月 19 日
用 Supabase @madewifsupabase 制作
我们在#madewithsupabase 上列出了一些新项目!万岁!查看 https://t.co/6ArG8G9ZdF ,或者您可以在下面的推文中看到它们
[
](https://twitter.com/intent/retweet?tweet_id u003d1428379387863306241)
更多推荐




所有评论(0)