upload/store/FlitterStore.js

/**
 * @module flitter-upload/store/FlitterStore
 */

const Store = require('./Store')
const path = require('path')
const mkdirp = require('mkdirp')
const fs = require('fs').promises
const uuid = require('uuid/v4')

/**
 * Local filesystem-backed file store provider.
 * @extends module:flitter-upload/store/Store~Store
 */
class FlitterStore extends Store {
    /**
     * The absolute path to the directory where uploads will be stored.
     * @type {string}
     */
    store_path = ''

    /**
     * Defines the services required by this store.
     * @returns {Array<string>}
     */
    static get services() {
        return [...super.services, 'utility', 'output', 'models']
    }

    /**
     * Initializes the store. Creates the store upload directory, if it does not already exist.
     * @returns {Promise<void>}
     */
    async init() {
        this.store_path = path.resolve(path.dirname(this.utility.root()), this.config.destination ? this.config.destination : './uploads')
        await mkdirp(this.store_path)
        this.output.info(`Created ${this.config.name} upload destination: ${this.store_path}`)
    }

    /**
     * Permanently store a temporarily uploaded file in this store.
     * @param {object} params
     * @param {string} params.temp_path - absolute path to the temporarily uploaded file
     * @param {string} params.original_name - the original upload name of the file
     * @param {string} params.mime_type - the MIME type of the file.
     * @returns {Promise<module:flitter-upload/model/File~File>}
     */
    async store({ temp_path, original_name, mime_type }) {
        const File = this.models.get('upload::File')
        const upload_name = uuid()
        await fs.copyFile(temp_path, this.filepath(upload_name))

        const f = new File({
            original_name,
            upload_name,
            mime_type,
            store: this.config.name,
            store_id: upload_name,
        })

        await f.save()
        return f
    }

    /**
     * Send the specified file as the data for the response.
     * Sets the appropriate Content-Type and Content-Disposition headers.
     * @param {module:flitter-upload/model/File~File} file - the file to send
     * @param {express/response} response - the response
     * @returns {Promise<void>}
     */
    async send_file(file, response) {
        const file_path = this.filepath(file.store_id)
        response.setHeader('Content-Type', file.mime_type)
        response.setHeader('Content-Disposition', `inline; filename="${file.original_name}";`)
        response.sendFile(file_path)
    }

    /**
     * Resolve the unique file name of a store file to its absolute path in the store.
     * @param {string} file_name - the UUID name of the file
     * @returns {string} - absolute path to the stored file
     */
    filepath(file_name) {
        return path.resolve(this.store_path, file_name)
    }
}

module.exports = exports = FlitterStore