Blob Storage
Getting Started
Enable the blob storage in your NuxtHub project by adding the blob property to the hub object in your nuxt.config.ts file.
export default defineNuxtConfig({
hub: {
blob: true
}
})

hubBlob()
Server composable that returns a set of methods to manipulate the blob storage.
list()
Returns a paginated list of blobs (metadata only).
export default eventHandler(async () => {
const { blobs } = await hubBlob().list({ limit: 10 })
return blobs
})
Params
The list options.
Return
Returns BlobListResult.
serve()
Returns a blob's data.
export default eventHandler(async (event) => {
const { pathname } = getRouterParams(event)
return hubBlob().serve(event, pathname)
})
Params
Handler's event, needed to set headers.
The name of the blob to serve.
Return
Returns the blob's raw data and sets Content-Type and Content-Length headers.
head()
Returns a blob's metadata.
const metadata = await hubBlob().head(pathname)
Params
The name of the blob to serve.
Return
Returns a BlobObject.
get()
Returns a blob body.
const blob = await hubBlob().get(pathname)
Params
The name of the blob to serve.
Return
Returns a Blob or null if not found.
put()
Uploads a blob to the storage.
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 hubBlob().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>
Params
The name of the blob to serve.
The blob's data.
The put options. Any other provided field will be stored in the blob's metadata.
Return
Returns a BlobObject.
del()
Delete a blob with its pathname.
export default eventHandler(async (event) => {
const { pathname } = getRouterParams(event)
await hubBlob().del(pathname)
return sendNoContent(event)
})
You can also delete multiple blobs at once by providing an array of pathnames:
await hubBlob().del(['images/1.jpg', 'images/2.jpg'])
delete() method as alias of del().Params
The name of the blob to serve.
Return
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.
export default eventHandler(async (event) => {
return hubBlob().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: {
contentType: ['image/jpeg', 'images/png'], // allowed types of the file
},
put: {
addRandomSuffix: true
}
})
})
Params
The form key to read the file from. Defaults to 'files'.
When true, the formKey field will be an array of Blob objects.
See ensureBlob() options for more details.
See put() options for more details.
Return
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.
export default eventHandler(async (event) => {
return await hubBlob().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.Params
The content type of the blob.
The content length of the blob.
If true, a random suffix will be added to the blob's name. Defaults to false.
createMultipartUpload()
handleMultipartUpload() method to handle the multipart upload request.Start a new multipart upload.
export default eventHandler(async (event) => {
const { pathname } = getRouterParams(event)
const mpu = await hubBlob().createMultipartUpload(pathname)
return {
uploadId: mpu.uploadId,
pathname: mpu.pathname,
}
})
Params
The name of the blob to serve.
The put options. Any other provided field will be stored in the blob's metadata.
Return
Returns a BlobMultipartUpload
resumeMultipartUpload()
handleMultipartUpload() method to handle the multipart upload request.Continue processing of unfinished multipart upload.
To upload a part of the multipart upload, you can use the uploadPart() method:
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 = hubBlob().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 = hubBlob().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 = hubBlob().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 },
},
)
}
Params
The name of the blob to serve.
The upload ID of the multipart upload.
Return
Returns a BlobMultipartUpload
Params
The event to handle.
ensureBlob()
ensureBlob() is a handy util to validate a Blob by checking its size and type:
// Will throw an error if the file is not an image or is larger than 1MB
ensureBlob(file, { maxSize: '1MB', types: ['image' ]})
Params
The file to validate.
Note that at least maxSize or types should be provided.
Return
Returns nothing.
Throws an error if file doesn't meet the requirements.
Vue Composables
server/ directory).useUpload()
useUpload is to handle file uploads in your Nuxt application.
<script setup lang="ts">
const upload = useUpload('/api/blob', { method: 'PUT' })
async function onFileSelect({ target }: Event) {
const uploadedFiles = await upload(target as HTMLInputElement)
// file uploaded successfully
}
</script>
<template>
<input
accept="jpeg, png"
type="file"
name="file"
multiple
@change="onFileSelect"
>
</template>
Params
The base URL of the upload API.
Optionally, you can pass Fetch options to the request. Read more about Fetch API here.
Return
Return a MultipartUpload function that can be used to upload a file in parts.
const { completed, progress, abort } = upload(file)
const data = await completed
useMultipartUpload()
Application composable that creates a multipart upload helper.
export const mpu = useMultipartUpload('/api/files/multipart')
Params
The base URL of the multipart upload API handled by handleMultipartUpload().
The options for the multipart upload helper.
Return
Return a MultipartUpload function that can be used to upload a file in parts.
const { completed, progress, abort } = mpu(file)
const data = await completed
Types
BlobObject
interface BlobObject {
pathname: string
contentType: string | undefined
size: number
uploadedAt: Date,
customMetadata?: Record<string, string> | undefined
}
BlobMultipartUpload
export interface BlobMultipartUpload {
pathname: string
uploadId: string
uploadPart(
partNumber: number,
value: string | ReadableStream<any> | ArrayBuffer | ArrayBufferView | Blob
): Promise<BlobUploadedPart>
abort(): Promise<void>
complete(uploadedParts: BlobUploadedPart[]): Promise<BlobObject>
}
BlobUploadedPart
export interface BlobUploadedPart {
partNumber: number;
etag: string;
}
MultipartUploader
export type MultipartUploader = (file: File) => {
completed: Promise<SerializeObject<BlobObject> | undefined>
progress: Readonly<Ref<number>>
abort: () => Promise<void>
}
BlobListResult
interface BlobListResult {
blobs: BlobObject[]
hasMore: boolean
cursor?: string
folders?: string[]
}
Examples
List blobs with pagination
export default eventHandler(async (event) => {
const { limit, cursor } = await getQuery(event)
return hubBlob().list({
limit: limit ? Number.parseInt(limit) : 10,
cursor: cursor ? cursor : undefined
})
})
