You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

289 lines
7.8 KiB

const { Schema, mongo } = require('mongoose')
const assert = require('assert')
const schemaOptions = {
versionKey: false,
collection: 'fs.files'
}
/**
* Mongoose schema for MongoDB GridFS
*
* ```javascript
* const mongoose = require('mongoose')
* const schema = require('gridfile')
*
* const GridFile = mongoose.model('GridFile', schema)
*
* const gridFile = new GridFile()
* ```
* @namespace GridFile
*/
const schema = new Schema({
/**
* @member {Number} GridFile#length
*/
length: { type: Number },
/**
* @member {Number} GridFile#chunkSize
*/
chunkSize: { type: Number },
/**
* @member {Date} GridFile#uploadDate
*/
uploadDate: { type: Date },
/**
* @member {String} GridFile#md5
*/
md5: { type: String },
/**
* A MD5 hash is auto-generated when a file is uploaded
* @member {String} GridFile#filename
*/
filename: { type: String },
/**
* Value is be used as `contentType` option when opening an upload stream: [GridFSBucket#openUploadStream](https://mongodb.github.io/node-mongodb-native/3.6/api/GridFSBucket.html#openUploadStream)
* @member {String} GridFile#contentType
*/
contentType: { type: String },
/**
* Value is be used as `metadata` option when opening an upload stream: [GridFSBucket#openUploadStream](https://mongodb.github.io/node-mongodb-native/3.6/api/GridFSBucket.html#openUploadStream)
* @member {Any} GridFile#metadata
*/
metadata: { type: Schema.Types.Mixed },
/**
* Value is be used as `aliases` option when opening an upload stream: [GridFSBucket#openUploadStream](https://mongodb.github.io/node-mongodb-native/3.6/api/GridFSBucket.html#openUploadStream)
* @member {[String]} GridFile#aliases
*/
aliases: [{ type: String }]
}, schemaOptions)
/* Document Properties */
/**
* Alias for GridFile#uploadDate
* @member {Date} GridFile#createdAt
*/
schema.virtual('createdAt').get(function () { return this.uploadDate })
/**
* @ignore
*/
schema.virtual('model').get(function () { return this.constructor })
/**
* Value is be used as `chunkSizeBytes` option when opening an upload stream: [GridFSBucket#openUploadStream](https://mongodb.github.io/node-mongodb-native/3.6/api/GridFSBucket.html#openUploadStream)
* @member {Number} GridFile#chunkSizeBytes
*/
schema.virtual('chunkSizeBytes')
/* Model Methods */
/**
* Get the GridFS bucket created from the Mongoose connection
* @member {Function} GridFile.getBucket
* @returns {GridFSBucket} GridFS Bucket
* @example
* const bucket = GridFile.getBucket()
*/
schema.static('getBucket', function () {
if (this.bucket) { } else {
// check the collection name
assert(this.collection.collectionName.endsWith('.files'), 'Collection Name doesn\'t end with .files')
// initialize gridfs bucket
const connection = this.db
this.bucket = new mongo.GridFSBucket(connection.db, {
bucketName: this.collection.collectionName.replace('.files', '')
})
}
return this.bucket
})
/**
* Delete a file from GridFS using [GridFSBucket#delete](https://mongodb.github.io/node-mongodb-native/3.6/api/GridFSBucket.html#delete)
* @member {Function} GridFile.findOneAndDelete
* @returns {Promise<GridFile>} Deleted GridFile as a Promise
* @example
* const deletedFile = await GridFile.findOneAndDelete({ filename: 'image.png' })
*/
schema.static('findOneAndDelete', async function (query) {
const doc = await this.findOne(query)
if (doc) await this.getBucket().delete(doc._id)
return doc
})
/**
* Delete a file from GridFS using [GridFSBucket#delete](https://mongodb.github.io/node-mongodb-native/3.6/api/GridFSBucket.html#delete)
* @member {Function} GridFile.findByIdAndDelete
* @returns {Promise<GridFile>} Deleted GridFile as a Promise
* @example
* const deletedFile = await GridFile.findByIdAndDelete('some-id')
*/
schema.static(`findByIdAndDelete`, function (id) {
return this.findOneAndDelete({ _id: id })
})
/* Document Methods */
/**
* Get a GridFS stream to upload a file
* @member {Function} GridFile#getUploadStream
* @returns {GridFSBucketWriteStream} Upload Stream
* @example
* const uploadStream = gridFile.getUploadStream()
*/
schema.method('getUploadStream', function () {
const bucket = this.model.getBucket()
// mongoose generates an id
return bucket.openUploadStreamWithId(this._id, this.filename, {
chunkSizeBytes: this.chunkSizeBytes,
metadata: this.metadata,
contentType: this.contentType,
aliases: this.aliases
})
})
/**
* Get a GridFS stream to download a file
* @member {Function} GridFile#getDownloadStream
* @returns {GridFSBucketReadStream} Download Stream
* @example
* const downloadStream = gridFile.getDownloadStream()
*/
schema.method('getDownloadStream', function () {
const bucket = this.model.getBucket()
return bucket.openDownloadStream(this._id)
})
/**
* Upload a file to GridFS
* @member {Function} GridFile#uploadStream
* @param {Stream} FileStream Read stream of file to upload
* @returns {GridFSBucketWriteStream} Upload Stream
* @example
* const fs = require('fs')
*
* const fileStream = fs.createReadStream('/path/to/file')
* const uploadStream = gridFile.uploadStream(fileStream)
*
* uploadStream.on('finish', (file) => {
* console.log(file)
* })
*/
schema.method('uploadStream', function (stream) {
const uploadStream = this.getUploadStream()
stream.pipe(uploadStream)
return uploadStream
})
/**
* Download a file from GridFS
* @member {Function} GridFile#downloadStream
* @param {Stream} FileStream Write stream of file to download into
* @returns {GridFSBucketWriteStream} Download Stream
* @example
* const fs = require('fs')
*
* const fileStream = fs.createWriteStream('/path/to/file')
* const DownloadStream = gridFile.downloadStream(fileStream)
*
* fileStream.on('finish', () => {
* console.log('File downloaded successfully')
* })
*/
schema.method('downloadStream', function (stream) {
const downloadStream = this.getDownloadStream()
downloadStream.pipe(stream)
return downloadStream
})
/**
* Upload a file to GridFS
* @member {Function} GridFile#upload
* @param {Stream} FileStream Read stream of file to upload
* @param {Function} Callback Callback function
* @returns {Promise<GridFile>} GridFile as a Promise
* @example
* const fs = require('fs')
*
* const fileStream = fs.createReadStream('/path/to/file')
* const uploadedFile = await gridFile.upload(fileStream)
* @example
* // callback
* gridFile.upload(filestream, (err, uploadedFile) => {
* if(err){
* console.error(err)
* } else {
* console.log(uploadedFile)
* }
* })
*/
schema.method('upload', function (stream, callback) {
return new Promise((resolve, reject) => {
const uploadStream = this.uploadStream(stream)
uploadStream.on('error', (error) => {
reject(error)
if (callback) callback(error)
})
uploadStream.on('finish', (file) => {
const document = this.model.hydrate(file)
resolve(document)
if (callback) callback(null, document)
})
})
})
/**
* Download a file from GridFS
* @member {Function} GridFile#download
* @param {Stream} FileStream Write stream of file to download into
* @param {Function} Callback Callback function
* @returns {Promise<Void>} Promise
* @example
* const fs = require('fs')
*
* const fileStream = fs.createWriteStream('/path/to/file')
* await gridFile.download(fileStream)
* @example
* // callback
* gridFile.download(fileStream, (err){
* if(err){
* console.error(err)
* } else {
* console.log('File downloaded successfully')
* }
* })
*/
schema.method('download', function (stream, callback) {
return new Promise((resolve, reject) => {
this.downloadStream(stream)
stream.on('error', (error) => {
reject(error)
if (callback) callback(error)
})
stream.on('finish', () => {
resolve()
if (callback) callback()
})
})
})
module.exports = schema