mirror of https://github.com/wekan/wekan
parent
536fb00d61
commit
44fd652b05
@ -0,0 +1,246 @@ |
||||
import fs from 'fs'; |
||||
import { createBucket } from './grid/createBucket'; |
||||
import { createObjectId } from './grid/createObjectId'; |
||||
import { createOnAfterUpload } from './fsHooks/createOnAfterUpload'; |
||||
import { createInterceptDownload } from './fsHooks/createInterceptDownload'; |
||||
import { createOnAfterRemove } from './fsHooks/createOnAfterRemove'; |
||||
|
||||
const insertActivity = (fileObj, activityType) => |
||||
Activities.insert({ |
||||
userId: fileObj.userId, |
||||
type: 'card', |
||||
activityType, |
||||
attachmentId: fileObj._id, |
||||
attachmentName: fileObj.name, |
||||
boardId: fileObj.meta.boardId, |
||||
cardId: fileObj.meta.cardId, |
||||
listId: fileObj.meta.listId, |
||||
swimlaneId: fileObj.meta.swimlaneId, |
||||
}); |
||||
|
||||
let attachmentBucket; |
||||
if (Meteor.isServer) { |
||||
attachmentBucket = createBucket('attachments'); |
||||
} |
||||
|
||||
/** Strategy to store attachments */ |
||||
class AttachmentStoreStrategy { |
||||
|
||||
/** constructor |
||||
* @param filesCollection the current FilesCollection instance |
||||
* @param fileObj the current file object |
||||
* @param versionName the current version |
||||
*/ |
||||
constructor(filesCollection, fileObj, versionName) { |
||||
this.filesCollection = filesCollection; |
||||
this.fileObj = fileObj; |
||||
this.versionName = versionName; |
||||
} |
||||
|
||||
/** after successfull upload */ |
||||
onAfterUpload() { |
||||
// If the attachment doesn't have a source field or its source is different than import
|
||||
if (!this.fileObj.meta.source || this.fileObj.meta.source !== 'import') { |
||||
// Add activity about adding the attachment
|
||||
insertActivity(this.fileObj, 'addAttachment'); |
||||
} |
||||
} |
||||
|
||||
/** download the file |
||||
* @param http the current http request |
||||
*/ |
||||
interceptDownload(http) { |
||||
} |
||||
|
||||
/** after file remove */ |
||||
onAfterRemove() { |
||||
insertActivity(this.fileObj, 'deleteAttachment'); |
||||
} |
||||
|
||||
/** returns a read stream |
||||
* @return the read stream |
||||
*/ |
||||
getReadStream() { |
||||
} |
||||
|
||||
/** returns a write stream |
||||
* @return the write stream |
||||
*/ |
||||
getWriteStream() { |
||||
} |
||||
|
||||
/** writing finished |
||||
* @param finishedData the data of the write stream finish event |
||||
*/ |
||||
writeStreamFinished(finishedData) { |
||||
} |
||||
|
||||
/** remove the file */ |
||||
unlink() { |
||||
} |
||||
|
||||
/** return the storage name |
||||
* @return the storage name |
||||
*/ |
||||
getStorageName() { |
||||
} |
||||
|
||||
static getFileStrategy(filesCollection, fileObj, versionName, storage) { |
||||
if (!storage) { |
||||
storage = fileObj.versions[versionName].storage || "gridfs"; |
||||
} |
||||
let ret; |
||||
if (["fs", "gridfs"].includes(storage)) { |
||||
if (storage == "fs") { |
||||
ret = new AttachmentStoreStrategyFilesystem(filesCollection, fileObj, versionName); |
||||
} else if (storage == "gridfs") { |
||||
ret = new AttachmentStoreStrategyGridFs(filesCollection, fileObj, versionName); |
||||
} |
||||
} |
||||
console.log("getFileStrategy: ", ret.constructor.name); |
||||
return ret; |
||||
} |
||||
} |
||||
|
||||
/** Strategy to store attachments at GridFS (MongoDB) */ |
||||
class AttachmentStoreStrategyGridFs extends AttachmentStoreStrategy { |
||||
|
||||
/** constructor |
||||
* @param filesCollection the current FilesCollection instance |
||||
* @param fileObj the current file object |
||||
* @param versionName the current version |
||||
*/ |
||||
constructor(filesCollection, fileObj, versionName) { |
||||
super(filesCollection, fileObj, versionName); |
||||
} |
||||
|
||||
/** after successfull upload */ |
||||
onAfterUpload() { |
||||
createOnAfterUpload(this.filesCollection, attachmentBucket, this.fileObj, this.versionName); |
||||
super.onAfterUpload(); |
||||
} |
||||
|
||||
/** download the file |
||||
* @param http the current http request |
||||
*/ |
||||
interceptDownload(http) { |
||||
const ret = createInterceptDownload(this.filesCollection, attachmentBucket, this.fileObj, http, this.versionName); |
||||
return ret; |
||||
} |
||||
|
||||
/** after file remove */ |
||||
onAfterRemove() { |
||||
this.unlink(); |
||||
super.onAfterRemove(); |
||||
} |
||||
|
||||
/** returns a read stream |
||||
* @return the read stream |
||||
*/ |
||||
getReadStream() { |
||||
const gridFsFileId = (this.fileObj.versions[this.versionName].meta || {}) |
||||
.gridFsFileId; |
||||
let ret; |
||||
if (gridFsFileId) { |
||||
const gfsId = createObjectId({ gridFsFileId }); |
||||
ret = attachmentBucket.openDownloadStream(gfsId); |
||||
} |
||||
return ret; |
||||
} |
||||
|
||||
/** returns a write stream |
||||
* @return the write stream |
||||
*/ |
||||
getWriteStream() { |
||||
const fileObj = this.fileObj; |
||||
const versionName = this.versionName; |
||||
const metadata = { ...fileObj.meta, versionName, fileId: fileObj._id }; |
||||
const ret = attachmentBucket.openUploadStream(this.fileObj.name, { |
||||
contentType: fileObj.type || 'binary/octet-stream', |
||||
metadata, |
||||
}); |
||||
return ret; |
||||
} |
||||
|
||||
/** writing finished |
||||
* @param finishedData the data of the write stream finish event |
||||
*/ |
||||
writeStreamFinished(finishedData) { |
||||
const gridFsFileIdName = this.getGridFsFileIdName(); |
||||
Attachments.update({ _id: this.fileObj._id }, { $set: { [gridFsFileIdName]: finishedData._id.toHexString(), } }); |
||||
} |
||||
|
||||
/** remove the file */ |
||||
unlink() { |
||||
createOnAfterRemove(this.filesCollection, attachmentBucket, this.fileObj, this.versionName); |
||||
const gridFsFileIdName = this.getGridFsFileIdName(); |
||||
Attachments.update({ _id: this.fileObj._id }, { $unset: { [gridFsFileIdName]: 1 } }); |
||||
} |
||||
|
||||
/** return the storage name |
||||
* @return the storage name |
||||
*/ |
||||
getStorageName() { |
||||
return "gridfs"; |
||||
} |
||||
|
||||
/** returns the property name of gridFsFileId |
||||
* @return the property name of gridFsFileId |
||||
*/ |
||||
getGridFsFileIdName() { |
||||
const ret = `versions.${this.versionName}.meta.gridFsFileId`; |
||||
return ret; |
||||
} |
||||
} |
||||
|
||||
/** Strategy to store attachments at filesystem */ |
||||
class AttachmentStoreStrategyFilesystem extends AttachmentStoreStrategy { |
||||
|
||||
/** constructor |
||||
* @param filesCollection the current FilesCollection instance |
||||
* @param fileObj the current file object |
||||
* @param versionName the current version |
||||
*/ |
||||
constructor(filesCollection, fileObj, versionName) { |
||||
super(filesCollection, fileObj, versionName); |
||||
} |
||||
|
||||
/** returns a read stream |
||||
* @return the read stream |
||||
*/ |
||||
getReadStream() { |
||||
const ret = fs.createReadStream(this.fileObj.versions[this.versionName].path) |
||||
return ret; |
||||
} |
||||
|
||||
/** returns a write stream |
||||
* @return the write stream |
||||
*/ |
||||
getWriteStream() { |
||||
const newFileName = this.fileObj.name; |
||||
const filePath = this.fileObj.versions[this.versionName].path; |
||||
const ret = fs.createWriteStream(filePath); |
||||
return ret; |
||||
} |
||||
|
||||
/** writing finished |
||||
* @param finishedData the data of the write stream finish event |
||||
*/ |
||||
writeStreamFinished(finishedData) { |
||||
} |
||||
|
||||
/** remove the file */ |
||||
unlink() { |
||||
const filePath = this.fileObj.versions[this.versionName].path; |
||||
fs.unlink(filePath, () => {}); |
||||
} |
||||
|
||||
/** return the storage name |
||||
* @return the storage name |
||||
*/ |
||||
getStorageName() { |
||||
return "fs"; |
||||
} |
||||
} |
||||
|
||||
export default AttachmentStoreStrategy; |
@ -1,17 +1,12 @@ |
||||
import { createObjectId } from '../grid/createObjectId'; |
||||
|
||||
export const createOnAfterRemove = bucket => |
||||
function onAfterRemove(files) { |
||||
files.forEach(file => { |
||||
Object.keys(file.versions).forEach(versionName => { |
||||
const gridFsFileId = (file.versions[versionName].meta || {}) |
||||
.gridFsFileId; |
||||
if (gridFsFileId) { |
||||
const gfsId = createObjectId({ gridFsFileId }); |
||||
bucket.delete(gfsId, err => { |
||||
// if (err) console.error(err);
|
||||
}); |
||||
} |
||||
export const createOnAfterRemove = |
||||
function onAfterRemove(filesCollection, bucket, file, versionName) { |
||||
const gridFsFileId = (file.versions[versionName].meta || {}) |
||||
.gridFsFileId; |
||||
if (gridFsFileId) { |
||||
const gfsId = createObjectId({ gridFsFileId }); |
||||
bucket.delete(gfsId, err => { |
||||
}); |
||||
}); |
||||
} |
||||
}; |
||||
|
@ -1,51 +1,42 @@ |
||||
import { Meteor } from 'meteor/meteor'; |
||||
import fs from 'fs'; |
||||
|
||||
export const createOnAfterUpload = bucket => |
||||
function onAfterUpload(file) { |
||||
const self = this; |
||||
|
||||
// here you could manipulate your file
|
||||
// and create a new version, for example a scaled 'thumbnail'
|
||||
// ...
|
||||
|
||||
// then we read all versions we have got so far
|
||||
Object.keys(file.versions).forEach(versionName => { |
||||
const metadata = { ...file.meta, versionName, fileId: file._id }; |
||||
fs.createReadStream(file.versions[versionName].path) |
||||
|
||||
// this is where we upload the binary to the bucket using bucket.openUploadStream
|
||||
// see http://mongodb.github.io/node-mongodb-native/3.2/api/GridFSBucket.html#openUploadStream
|
||||
.pipe( |
||||
bucket.openUploadStream(file.name, { |
||||
contentType: file.type || 'binary/octet-stream', |
||||
metadata, |
||||
}), |
||||
) |
||||
|
||||
// and we unlink the file from the fs on any error
|
||||
// that occurred during the upload to prevent zombie files
|
||||
.on('error', err => { |
||||
console.error("[createOnAfterUpload error]", err); |
||||
self.unlink(this.collection.findOne(file._id), versionName); // Unlink files from FS
|
||||
}) |
||||
|
||||
// once we are finished, we attach the gridFS Object id on the
|
||||
// FilesCollection document's meta section and finally unlink the
|
||||
// upload file from the filesystem
|
||||
.on( |
||||
'finish', |
||||
Meteor.bindEnvironment(ver => { |
||||
const property = `versions.${versionName}.meta.gridFsFileId`; |
||||
|
||||
self.collection.update(file._id, { |
||||
$set: { |
||||
[property]: ver._id.toHexString(), |
||||
}, |
||||
}); |
||||
|
||||
self.unlink(this.collection.findOne(file._id), versionName); // Unlink files from FS
|
||||
}), |
||||
); |
||||
}); |
||||
}; |
||||
export const createOnAfterUpload = function onAfterUpload(filesCollection, bucket, file, versionName) { |
||||
const self = filesCollection; |
||||
const metadata = { ...file.meta, versionName, fileId: file._id }; |
||||
fs.createReadStream(file.versions[versionName].path) |
||||
|
||||
// this is where we upload the binary to the bucket using bucket.openUploadStream
|
||||
// see http://mongodb.github.io/node-mongodb-native/3.2/api/GridFSBucket.html#openUploadStream
|
||||
.pipe( |
||||
bucket.openUploadStream(file.name, { |
||||
contentType: file.type || 'binary/octet-stream', |
||||
metadata, |
||||
}), |
||||
) |
||||
|
||||
// and we unlink the file from the fs on any error
|
||||
// that occurred during the upload to prevent zombie files
|
||||
.on('error', err => { |
||||
console.error("[createOnAfterUpload error]", err); |
||||
self.unlink(self.collection.findOne(file._id), versionName); // Unlink files from FS
|
||||
}) |
||||
|
||||
// once we are finished, we attach the gridFS Object id on the
|
||||
// FilesCollection document's meta section and finally unlink the
|
||||
// upload file from the filesystem
|
||||
.on( |
||||
'finish', |
||||
Meteor.bindEnvironment(ver => { |
||||
const property = `versions.${versionName}.meta.gridFsFileId`; |
||||
|
||||
self.collection.update(file._id, { |
||||
$set: { |
||||
[property]: ver._id.toHexString(), |
||||
}, |
||||
}); |
||||
|
||||
self.unlink(self.collection.findOne(file._id), versionName); // Unlink files from FS
|
||||
}), |
||||
); |
||||
}; |
||||
|
Loading…
Reference in new issue