On this page

File management

Pruvious allows you to effortlessly upload files either in a local directory within your project or on a remote S3 server.


Example

Here's an example of how to upload an image from the client to the server using a custom component and API route:

# components/UploadAvatar.vue

<template>
  <div>
    <form @submit.prevent="submit">
      <input name="$file" type="file" />
      <button type="submit">Upload</button>
    </form>

    <p v-if="error" class="text-red-500">{{ error }}</p>
    <img v-if="imageSrc" :src="imageSrc" />
  </div>
</template>

<script lang="ts" setup>
import { isString } from '#pruvious'

const error = ref('')
const imageSrc = ref('')

async function submit(event: Event) {
  error.value = ''

  await $fetch<string>('/api/avatar', {
    method: 'post',
    body: new FormData(event.target as HTMLFormElement),
  })
    .then((res) => {
      imageSrc.value = res
    })
    .catch(({ data }) => {
      error.value = isString(data) ? data : JSON.stringify(data)
    })
}
</script>

Now, let's create a server handler that will store the image in the database and a storage drive.

# server/api/avatar.post.ts

import { getPublicFilePath, imageTypes, isFile } from '#pruvious'
import { query, readInputData } from '#pruvious/server'

export default defineEventHandler(async (event) => {
  const input = await readInputData(event, 'uploads')

  if (input.errors.length) {
    setResponseStatus(event, 400)
    return input.errors.join('\n')
  }

  if (Array.isArray(input.data)) {
    setResponseStatus(event, 400)
    return 'You can only upload one file at a time'
  } else if (isFile(input.data.$file) && !imageTypes.includes(input.data.$file.type)) {
    setResponseStatus(event, 422)
    return { $file: 'The file must be an image' }
  }

  const result = await query('uploads').create({
    $file: input.data.$file!,
    directory: 'avatars',
  })

  if (result.success) {
    return getPublicFilePath(result.record, useRuntimeConfig())
  } else if (result.message) {
    setResponseStatus(event, 400)
    return result.message
  } else {
    setResponseStatus(event, 422)
    return result.errors
  }
})

Pruvious uses a special $file field to associate a File with an uploads record. This field is only accessible during record creation. Once a $file is linked to an uploads record, it cannot be modified. To make changes, you need to create a new record and delete the old one.

Image optimization

Pruvious uses the sharp library to manipulate images in real-time. When working with image fields, the images are automatically optimized according to the specified sources option. If you wish to manually transform an image, you can use the getOptimizedImage function.

In the following example, we convert the uploaded image to webp and include the absolute path (for local storage drive) or URL (for S3 drive) of the optimized image in the response.

# server/api/avatar.post.ts

import { imageTypes, isFile } from '#pruvious'
import { getOptimizedImage, query, readInputData } from '#pruvious/server'

export default defineEventHandler(async (event) => {
  const input = await readInputData(event, 'uploads')

  if (input.errors.length) {
    setResponseStatus(event, 400)
    return input.errors.join('\n')
  }

  if (Array.isArray(input.data)) {
    setResponseStatus(event, 400)
    return 'You can only upload one file at a time'
  } else if (isFile(input.data.$file) && !imageTypes.includes(input.data.$file.type)) {
    setResponseStatus(event, 422)
    return { $file: 'The file must be an image' }
  }

  const result = await query('uploads').create({
    $file: input.data.$file!,
    directory: 'avatars',
  })

  if (result.success) {
    const webp = await getOptimizedImage(result.record, { format: 'webp' })

    if (webp.success) {
      return webp.src
    } else {
      setResponseStatus(event, 400)
      return webp.error
    }
  } else if (result.message) {
    setResponseStatus(event, 400)
    return result.message
  } else {
    setResponseStatus(event, 422)
    return result.errors
  }
})

When the original upload record is deleted, all optimized images are also removed.

Local storage

The local storage is the default storage drive. It stores all uploaded files in the .uploads directory in your project's root. You can customize the default options in your nuxt.config.ts file.

# nuxt.config.ts

export default defineNuxtConfig({
  modules: ['pruvious'],
  pruvious: {
    uploads: {
      drive: {
        type: 'local',
        path: './.uploads',
        urlPrefix: 'uploads',
      },
    },
  },
})

S3 storage

The S3 drive utilizes Amazon S3 cloud storage for file reading and writing. You can configure it in your nuxt.config.ts file as follows:

# nuxt.config.ts

export default defineNuxtConfig({
  modules: ['pruvious'],
  pruvious: {
    uploads: {
      drive: {
        type: 's3',
        baseUrl: 'https://your-space-here.fra1.digitaloceanspaces.com',
        bucket: 'your-space-here',
        endpoint: 'fra1.digitaloceanspaces.com',
        key: 'access-key',
        region: 'fra1',
        secret: 'secret-key',
      }
    },
  },
})

Maximum upload size

You can set the maximum upload size in your nuxt.config.ts file. Here's an example:

# nuxt.config.ts

export default defineNuxtConfig({
  modules: ['pruvious'],
  pruvious: {
    uploads: {
      maxFileSize: '16 MB',
    },
  },
})

Last updated on March 18, 2024 at 17:58