Blob
Store and upload images, videos and other unstructured data in your Nuxt application.
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.
export default eventHandler(async () => {
return hubBlob().list()
})
Params
The list options.
The maximum number of blobs to return per request. Defaults to 1000
.
Filters the results to only those that begin with the specified prefix.
The cursor to continue from a previous list operation.
If true
, the list will be folded using /
separator and list of folders will be returned.
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 blob = await hubBlob().head(pathname)
Params
The name of the blob to serve.
Return
Returns a BlobObject
.
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.
The content type of the blob. If not given, it will be inferred from the Blob or the file extension.
The content length of the blob.
If true
, a random suffix will be added to the blob's name. Defaults to false
.
The prefix to use for the blob pathname.
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()
handleUpload
is a all in one function to validate a Blob
by checking its size and type and upload it to the storage.
It can 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
contentType: ['image/jpeg', 'images/png'], // allowed types of the file
})
})
Params
The form key to read the file from. Defaults to 'files'
.
When true
, the formKey
field will be an array of Blob
objects.
The maximum size of the file, should be:
(1
| 2
| 4
| 8
| 16
| 32
| 64
| 128
| 256
| 512
| 1024
) + (B
| KB
| MB
| GB
)
e.g. '512KB'
, '1MB'
, '2GB'
, etc.
Allowed types of the file, e.g. ['image/jpeg']
.
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
.
The prefix to use for the blob pathname.
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.
The content type of the blob. If not given, it will be inferred from the Blob or the file extension.
The content length of the blob.
If true
, a random suffix will be added to the blob's name. Defaults to true
.
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.
The maximum size of the file, should be:
(1
| 2
| 4
| 8
| 16
| 32
| 64
| 128
| 256
| 512
| 1024
) + (B
| KB
| MB
| GB
)
e.g. '512KB'
, '1MB'
, '2GB'
, etc.
Allowed types of the file, e.g. ['image/jpeg']
.
Return
Returns nothing.
Throws an error if file
doesn't meet the requirements.
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.
The key to add the file/files to the request form. Defaults to 'files'
.
Whether to allow multiple files to be uploaded. Defaults to true
.
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.
The size of each part of the file to be uploaded. Defaults to 10MB
.
The maximum number of concurrent uploads. Defaults to 1
.
The maximum number of retry attempts for the whole upload. Defaults to 3
.
The prefix to use for the blob pathname.
Override the ofetch options.
The query
and headers
will be merged with the options provided by the uploader.
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
}
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
})
})