1.2.4 • Published 10 months ago

nuxt-file-save v1.2.4

Weekly downloads
-
License
MIT
Repository
github
Last release
10 months ago

Nuxt File Save

npm version npm downloads License Nuxt

A nuxt plugin for file uploading to local disk

Features

  •  Configurable, support for multi-file upload
  • 🚠  The type and size of the file are checked on the back end
  • 🌲  Save to the local disk

Quick Setup

Install the module to your Nuxt application with one command:

npx nuxi module add nuxt-file-save

Or add it manually to your nuxt.config.ts:

# Using pnpm
pnpm add -D nuxt-file-save

# Using yarn
yarn add --dev nuxt-file-save

# Using npm
npm install --save-dev nuxt-file-save
export default defineNuxtConfig({
    modules: ['nuxt-file-save'],
    fileSave: {
        // The location where the file is saved to disk, public by default, in the public folder at the root of the current project
        mount:'public',
        // Verify the configuration item associated with the upload file, overwriting it in the 'useFileVerify' method
        // options?: BlobUploadOptions
    }
})

That's it! You can now use Nuxt File Save in your Nuxt app ✨

Usage

Handling Files in the frontend

Just send the formData format data to the backend

<template>
    <input type="file" multiple @change="handleFileChange" />
</template>

<script setup>
const handleFileChange = (e: any) => {
    const files = e.target.files[0]
    if (!files) return

    const formData = new FormData()
    formData.append('files', files.value)

    // You can also verify before uploading the file
    // try {
    //     await useFileVerify(formData, {
    //         ensure: {
    //             maxSize: '100MB',
    //             types: ['audio', 'image', 'video', 'pdf', 'zip'],
    //         },
    //     })
    // } catch (error) {
    //     alert(error)
    //     return
    // }
    const res = await $fetch<{code: number, data: string, msg: string}>('/api/upload', {
        method: 'POST',
        body: formData,
    })
    console.log(res)
    if(res.code !== 200){ // fail
        return alert(res.msg)
    }

}
</script>

Handling files in the backend

On the server side, the useFileVerify method is used to verify the file, and the useFileSave method is used to save it

// server/api/upload.ts

export default defineEventHandler(async (event) => {
    try {
        const form = await readFormData(event)

        // multiple
        // const files = await useFileVerify(form, {
        //     multiple: 3, // Max 3 files at a time for now
        //     ensure: {
        //         maxSize: '50MB', // Max 50 MB each file
        //         types: ['audio', 'csv', 'image', 'video', 'pdf', 'text'], // Only allow these file types
        //     },
        // });

        // for (const file of files) {
        //     await useFileSave(file);
        // }

        // single
        const [file] = await useFileVerify(form, {
            formKey: 'files', // The key of the form data
            multiple: false, // Only allow one file at a time
            ensure: {
                maxSize: '256MB',
                types: ['audio', 'csv', 'image', 'video', 'pdf', 'text', 'zip', 'exe'],
            },
            lang: 'en', // Language for error messages
        })

        const date = new Date()
        // Generate folders based on time
        const dateDir = date.toLocaleDateString('zh-cn') // 2023/01/02
        // const dateDir = `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`
        const randomStr = Math.random().toString(36).substring(2, 6 + 2) // Random String 6 bits
        const fileDir = `/upload/${dateDir}`
        const url = await useFileSave(file, `${Date.now()}-${randomStr}`, fileDir)
        if (!url) return { code: 500, msg: 'error' }
        return { code: 200, msg: 'success', data: url }
    } catch (error: any) {    // eslint-disable-line
        return { code: error.statusCode, msg: error.message }
    }
})

If you don't want to check the file, just save the file

const form = await readFormData(event)
const [file] = form.getAll('files') as File[]
if (file) {
    const url = await useFileSave(file)
}

Method

useFileVerify(form: FormData, options?: BlobUploadOptions)

A method of receiving and validating files

  • form: FormData object
  • options: BlobUploadOptions object

Options:

  • formKey: The key of the form data, default is files
  • multiple: Whether to allow multiple files, The type is number | boolean, default is true
  • ensure: The file validation rules, File size and file type
  • lang: The language for error messages, default is en

Returns:

  • An array of File objects, If validation fails and an error message is thrown, you can use try.catch

Example:

// multiple
const files = await useFileVerify(form, {
    multiple: 3, // Max 3 files at a time for now
    ensure: {
        maxSize: '50MB', // Max 50 MB each file
        types: ['audio', 'csv', 'image', 'video', 'pdf', 'text'], // Only allow these file types
    },
});

try{
    // single
    const [file] = await useFileVerify(form, {
        formKey: 'files', // The key of the form data
        multiple: false, // Only allow one file at a time
        ensure: {
            maxSize: '256MB',
            types: ['audio', 'csv', 'image', 'video', 'pdf', 'text', 'zip', 'exe'],
        },
        lang: 'en', // Language for error messages
    })
}catch(e){
    // error info
}

useFileSave(file: File, fileName = file.name, fileDir?: string)

A method of uploading files.

  • file is the file object data.
  • fileName is the fileName, which defaults to the original fileName, or you can use a string with no suffix.
  • fileDir is the directory where the file is stored.

Returns:

  • The file url, Also using try.catch gets the error message

Example:

const url = await useFileSave(file, `${Date.now()}`, 'upload')

Type Declarations

export type FileSizeUnit = 'B' | 'KB' | 'MB' | 'GB' | 'TB'
export type BlobSize = `${number}${FileSizeUnit}`
export type BlobType =
    | 'image'
    | 'video'
    | 'audio'
    | 'pdf'
    | 'csv'
    | 'text'
    | 'blob'
    | (string & Record<never, never>)
export type MessageLangType = 'en'|'zh'
export interface BlobUploadOptions {
    /**
     * The key to get the file/files from the request form.
     * @default 'files'
     */
    formKey?: string
    /**
     * Whether to allow multiple files to be uploaded.
     * @default true
     */
    multiple?: boolean | number
    /**
     * Options used for the ensure() method.
     */
    ensure?: BlobEnsureOptions
    /**
     * The language in which the text message corresponds
     * @default 'en'
     */
    lang?: MessageLangType
}

export interface BlobEnsureOptions {
    /**
     * The maximum size of the blob (e.g. '1MB')
     */
    maxSize?: BlobSize
    /**
     * The allowed types of the blob (e.g. ['image/png', 'application/json', 'video'])
     */
    types?: BlobType[]
}

Error message code

codemessage
1000Received invalid file
1001Invalid file type. Only allowed: {{types}}
1002File too heavy. Max size is: {{maxSize}}
1003No files received
1004Multiple files are not allowed
1005Number of files exceeded, Maximum allowed: {{multiple}}
1006Invalid file size format: {{blobSize}}
1007Invalid file size unit: {{sizeUnit}}
1008Error uploading file

And that's it! Now you can use it happily in your project ✨

Cautions (production environment related)

(node:24872) ExperimentalWarning: buffer.File is an experimental feature and might change at any time

  • The message you are receiving isn't an issue, it's a warning emitted from the use of an experimental feature.

This library saves files to the public folder, so we can access them directly in the project.for example:

<img src="/upload/1678438436123.png" alt="image" />

But in a production environment, when we use nuxt build to build a project, the /public file will be copied to .output/public(which will result in an increase in disk space), but in reality, the file will only be saved to /public.

For production environment problem, I suggest using nginx to configure proxies to solve them

Run the project using PM2

// ecosystem.config.js  
// or 
// ecosystem.config.cjs 
module.exports = {
    apps: [
        {
            name: 'nuxt-web', // Sets the name of the PM2 process
            port: '8000', // The port on which the project runs
            exec_mode: 'cluster',
            instances: 'max',
            script: './.output/server/index.mjs',
            env: {
            },
        },
    ],
}
  

Run the project, if filename is ecosystem.config.js, ecosystem.config.js can be omitted

pm2 start ecosystem.config.js 
# or 
pm2 start ecosystem.config.cjs

Nginx configuration

# nuxt-web.conf
server {
    listen 80;
    server_name your-domain.com www.your-domain.com;

    # Set variables, project root directory
    set $base_path /home/project/nuxt-web;

    location / {
        root  $base_path;
        proxy_pass   http://127.0.0.1:8000;
    }
    
    #############################################################
    # Change the directory accessed by `/upload`, Example of `useFileSave(file, '', '/upload')` with the third parameter '/upload'
    location ^~/upload {
        alias $base_path/public/upload; # Set access to file directory
        autoindex on;
        autoindex_exact_size off; # Display file size in MB, GB format instead of KB
        autoindex_localtime on; # The file time is the time of the server
        if ($request_filename ~* ^.*?\.(txt|doc|pdf|rar|gz|zip|docx|exe|xlsx|ppt|pptx|png|jpg|gif|webp)$){
            add_header Content-Disposition attachment;
            add_header  Content-Type application/octet-stream;
        } # These files with suffixes can be downloaded
    }
    #############################################################
}

At this point, the access path of /upload has been changed, and files uploaded in the production environment can still be accessed normally. The .output/public/upload folder can also be deleted or not

Contribution

Run into a problem? Open a new issue. I'll try my best to include all the features requested if it is fitting to the scope of the project.

Want to add some feature? PRs are welcome!

  • Clone this repository
  • install the dependencies
  • prepare the project
  • run dev server

git clone https://github.com/nowo/nuxt-file-save && cd nuxt-file-save
pnpm i
pnpm run dev:prepare
pnpm run dev

License

MIT - Copyright (c) 2024 nowo

1.2.4

10 months ago

1.2.3

11 months ago

1.2.2

11 months ago

1.2.1

11 months ago

1.2.0

12 months ago

1.1.1

12 months ago

1.1.1-0

12 months ago

1.1.0

12 months ago

1.0.1

1 year ago