The hub:blob module provides access to the Blob storage through an unstorage instance.
import { blob } from 'hub:blob'
blob is auto-imported on server-side, you can directly use it without importing it from hub:blob.list()Returns a paginated list of blobs (metadata only).
import { blob } from 'hub:blob'
export default eventHandler(async () => {
const { blobs } = await blob.list({ limit: 10 })
return blobs
})
limit option is ignored and all blobs are returned.1000.true, the list will be folded using / separator and list of folders will be returned.Returns BlobListResult.
To fetch all blobs, you can use a while loop to fetch the next page until the cursor is null.
import { blob } from 'hub:blob'
let blobs = []
let cursor = null
do {
const res = await blob.list({ cursor })
blobs.push(...res.blobs)
cursor = res.cursor
} while (cursor)
serve()Returns a blob's data and sets Content-Type, Content-Length and ETag headers.
import { blob } from 'hub:blob'
export default eventHandler(async (event) => {
const { pathname } = getRouterParams(event)
return blob.serve(event, pathname)
})
<template>
<img src="/images/my-image.jpg">
</template>
You can also set a Content-Security-Policy header to add an additional layer of security:
import { blob } from 'hub:blob'
export default eventHandler(async (event) => {
const { pathname } = getRouterParams(event)
setHeader(event, 'Content-Security-Policy', 'default-src \'none\';')
return blob.serve(event, pathname)
})
Returns the blob's raw data and sets Content-Type and Content-Length headers.
head()Returns a blob's metadata.
const metadata = await blob.head(pathname)
Returns a BlobObject.
get()Returns a blob body.
const file = await blob.get(pathname)
Returns a Blob or null if not found.
put()Uploads a blob to the storage.
import { blob, ensureBlob } from 'hub:blob'
export default eventHandler(async (event) => {
const form = await readFormData(event)
const file = form.get('file') as File
if (!file || !file.size) {
throw createError({ statusCode: 400, message: 'No file provided' })
}
ensureBlob(file, {
maxSize: '1MB',
types: ['image']
})
return blob.put(file.name, file, {
addRandomSuffix: false,
prefix: 'images'
})
})
See an example on the Vue side:
<script setup lang="ts">
async function uploadImage (e: Event) {
const form = e.target as HTMLFormElement
await $fetch('/api/files', {
method: 'POST',
body: new FormData(form)
}).catch((err) => alert('Failed to upload image:\n'+ err.data?.message))
form.reset()
}
</script>
<template>
<form @submit.prevent="uploadImage">
<label>Upload an image: <input type="file" name="image"></label>
<button type="submit">
Upload
</button>
</form>
</template>
'public' or 'private'. Note that only S3 driver supports this option currently.true, a random suffix will be added to the blob's name. Defaults to false.Returns a BlobObject.
del()Delete a blob with its pathname.
import { blob } from 'hub:blob'
export default eventHandler(async (event) => {
const { pathname } = getRouterParams(event)
await blob.del(pathname)
return sendNoContent(event)
})
You can also delete multiple blobs at once by providing an array of pathnames:
await blob.del(['images/1.jpg', 'images/2.jpg'])
delete() method as alias of del().Returns nothing.
handleUpload()This is an "all in one" function to validate a Blob by checking its size and type and upload it to the storage.
useUpload() Vue composable.It can be used to handle file uploads in API routes.
import { blob } from 'hub:blob'
export default eventHandler(async (event) => {
return blob.handleUpload(event, {
formKey: 'files', // read file or files form the `formKey` field of request body (body should be a `FormData` object)
multiple: true, // when `true`, the `formKey` field will be an array of `Blob` objects
ensure: {
types: ['image/jpeg', 'image/png'], // allowed types of the file
},
put: {
addRandomSuffix: true
}
})
})
<script setup lang="ts">
const upload = useUpload('/api/blob', { method: 'PUT' })
async function onFileSelect(event: Event) {
const uploadedFiles = await upload(event.target as HTMLInputElement)
// file uploaded successfully
}
</script>
<template>
<input type="file" name="file" @change="onFileSelect" multiple accept="image/jpeg, image/png" />
</template>
'files'.true, the formKey field will be an array of Blob objects.ensureBlob() options for more details.put() options for more details.Returns a BlobObject or an array of BlobObject if multiple is true.
Throws an error if file doesn't meet the requirements.
handleMultipartUpload()Handle the request to support multipart upload.
import { blob } from 'hub:blob'
export default eventHandler(async (event) => {
return await blob.handleMultipartUpload(event)
})
[action] and [...pathname] params.On the client side, you can use the useMultipartUpload() composable to upload a file in parts.
<script setup lang="ts">
async function uploadFile(file: File) {
const upload = useMultipartUpload('/api/files/multipart')
const { progress, completed, abort } = upload(file)
}
</script>
useMultipartUpload() on usage details.true, a random suffix will be added to the blob's name. Defaults to false.createMultipartUpload()handleMultipartUpload() to handle the multipart upload requests.Start a new multipart upload.
import { blob } from 'hub:blob'
export default eventHandler(async (event) => {
const { pathname } = getRouterParams(event)
const mpu = await blob.createMultipartUpload(pathname)
return {
uploadId: mpu.uploadId,
pathname: mpu.pathname,
}
})
true, a random suffix will be added to the blob's name. Defaults to true.Returns a BlobMultipartUpload
resumeMultipartUpload()handleMultipartUpload() to handle the multipart upload requests.Continue processing of unfinished multipart upload.
To upload a part of the multipart upload, you can use the uploadPart() method:
import { blob } from 'hub:blob'
export default eventHandler(async (event) => {
const { pathname } = getRouterParams(event)
const { uploadId, partNumber } = getQuery(event)
const stream = getRequestWebStream(event)!
const body = await streamToArrayBuffer(stream, contentLength)
const mpu = blob.resumeMultipartUpload(pathname, uploadId)
return await mpu.uploadPart(partNumber, body)
})
Complete the upload by calling complete() method:
export default eventHandler(async (event) => {
const { pathname, uploadId } = getQuery(event)
const parts = await readBody(event)
const mpu = blob.resumeMultipartUpload(pathname, uploadId)
return await mpu.complete(parts)
})
If you want to cancel the upload, you need to call abort() method:
export default eventHandler(async (event) => {
const { pathname } = getRouterParams(event)
const { uploadId } = getQuery(event)
const mpu = blob.resumeMultipartUpload(pathname, uploadId)
await mpu.abort()
return sendNoContent(event)
})
A simple example of multipart upload in client with above routes:
async function uploadLargeFile(file: File) {
const chunkSize = 10 * 1024 * 1024 // 10MB
const count = Math.ceil(file.size / chunkSize)
const { pathname, uploadId } = await $fetch(
`/api/files/multipart/${file.name}`,
{ method: 'POST' },
)
const uploaded = []
for (let i = 0; i < count; i++) {
const start = i * chunkSize
const end = Math.min(start + chunkSize, file.size)
const partNumber = i + 1
const chunk = file.slice(start, end)
const part = await $fetch(
`/api/files/multipart/${pathname}`,
{
method: 'PUT',
query: { uploadId, partNumber },
body: chunk,
},
)
uploaded.push(part)
}
return await $fetch(
'/api/files/multipart/complete',
{
method: 'POST',
query: { pathname, uploadId },
body: { parts: uploaded },
},
)
}
Returns a BlobMultipartUpload
Setup
Configure Blob Storage in your Nuxt application, including installation, environment variables, and initial setup for uploading and serving files.
Setup
Configure caching in your Nuxt application, including setup for pages, API routes, and serverless functions to improve performance and reduce load.