add-file-to-drive - 見通しを良くする
This commit is contained in:
		| @@ -32,29 +32,18 @@ const addToGridFS = (name: string, readable: stream.Readable, type: string, meta | |||||||
| 			readable.pipe(writeStream); | 			readable.pipe(writeStream); | ||||||
| 		})); | 		})); | ||||||
|  |  | ||||||
| /** | const addFile = async ( | ||||||
|  * Add file to drive |  | ||||||
|  * |  | ||||||
|  * @param user User who wish to add file |  | ||||||
|  * @param file File path or readableStream |  | ||||||
|  * @param comment Comment |  | ||||||
|  * @param type File type |  | ||||||
|  * @param folderId Folder ID |  | ||||||
|  * @param force If set to true, forcibly upload the file even if there is a file with the same hash. |  | ||||||
|  * @return Object that represents added file |  | ||||||
|  */ |  | ||||||
| export default ( |  | ||||||
| 	user: any, | 	user: any, | ||||||
| 	file: string | stream.Readable, | 	file: string | stream.Readable, | ||||||
| 	name: string = null, | 	name: string = null, | ||||||
| 	comment: string = null, | 	comment: string = null, | ||||||
| 	folderId: mongodb.ObjectID = null, | 	folderId: mongodb.ObjectID = null, | ||||||
| 	force: boolean = false | 	force: boolean = false | ||||||
| ) => new Promise<any>((resolve, reject) => { | ) => { | ||||||
| 	log(`registering ${name} (user: ${user.username})`); | 	log(`registering ${name} (user: ${user.username})`); | ||||||
|  |  | ||||||
| 	// Get file path | 	// Get file path | ||||||
| 	new Promise((res: (v: string) => void, rej) => { | 	const path = await new Promise((res: (v: string) => void, rej) => { | ||||||
| 		if (typeof file === 'string') { | 		if (typeof file === 'string') { | ||||||
| 			res(file); | 			res(file); | ||||||
| 			return; | 			return; | ||||||
| @@ -75,141 +64,155 @@ export default ( | |||||||
| 				.catch(rej); | 				.catch(rej); | ||||||
| 		} | 		} | ||||||
| 		rej(new Error('un-compatible file.')); | 		rej(new Error('un-compatible file.')); | ||||||
| 	}) | 	}); | ||||||
| 		// Calculate hash, get content type and get file size |  | ||||||
| 		.then(path => Promise.all([ |  | ||||||
| 			path, |  | ||||||
| 			// hash |  | ||||||
| 			((): Promise<string> => new Promise((res, rej) => { |  | ||||||
| 				const readable = fs.createReadStream(path); |  | ||||||
| 				const hash = crypto.createHash('md5'); |  | ||||||
| 				const chunks = []; |  | ||||||
| 				readable |  | ||||||
| 					.on('error', rej) |  | ||||||
| 					.pipe(hash) |  | ||||||
| 					.on('error', rej) |  | ||||||
| 					.on('data', (chunk) => chunks.push(chunk)) |  | ||||||
| 					.on('end', () => { |  | ||||||
| 						const buffer = Buffer.concat(chunks); |  | ||||||
| 						res(buffer.toString('hex')); |  | ||||||
| 					}); |  | ||||||
| 			}))(), |  | ||||||
| 			// mime |  | ||||||
| 			((): Promise<[string, string | null]> => new Promise((res, rej) => { |  | ||||||
| 				const readable = fs.createReadStream(path); |  | ||||||
| 				readable |  | ||||||
| 					.on('error', rej) |  | ||||||
| 					.once('data', (buffer: Buffer) => { |  | ||||||
| 						readable.destroy(); |  | ||||||
| 						const type = fileType(buffer); |  | ||||||
| 						if (!type) { |  | ||||||
| 							return res(['application/octet-stream', null]); |  | ||||||
| 						} |  | ||||||
| 						return res([type.mime, type.ext]); |  | ||||||
| 					}); |  | ||||||
| 			}))(), |  | ||||||
| 			// size |  | ||||||
| 			((): Promise<number> => new Promise((res, rej) => { |  | ||||||
| 				fs.stat(path, (err, stats) => { |  | ||||||
| 					if (err) return rej(err); |  | ||||||
| 					res(stats.size); |  | ||||||
| 				}); |  | ||||||
| 			}))() |  | ||||||
| 		])) |  | ||||||
| 		.then(async ([path, hash, [mime, ext], size]) => { |  | ||||||
| 			log(`hash: ${hash}, mime: ${mime}, ext: ${ext}, size: ${size}`); |  | ||||||
|  |  | ||||||
| 			// detect name |  | ||||||
| 			const detectedName: string = name || (ext ? `untitled.${ext}` : 'untitled'); |  | ||||||
|  |  | ||||||
| 			if (!force) { |  | ||||||
| 				// Check if there is a file with the same hash |  | ||||||
| 				const much = await DriveFile.findOne({ |  | ||||||
| 					md5: hash, |  | ||||||
| 					'metadata.user_id': user._id |  | ||||||
| 				}); |  | ||||||
|  |  | ||||||
| 				if (much !== null) { |  | ||||||
| 					log('file with same hash is found'); |  | ||||||
| 					return resolve(much); |  | ||||||
| 				} else { |  | ||||||
| 					log('file with same hash is not found'); |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			const [properties, folder] = await Promise.all([ |  | ||||||
| 				// properties |  | ||||||
| 				(async () => { |  | ||||||
| 					if (!/^image\/.*$/.test(mime)) { |  | ||||||
| 						return null; |  | ||||||
| 					} |  | ||||||
| 					// If the file is an image, calculate width and height to save in property |  | ||||||
| 					const g = gm(fs.createReadStream(path), name); |  | ||||||
| 					const size = await prominence(g).size(); |  | ||||||
| 					const properties = { |  | ||||||
| 						width: size.width, |  | ||||||
| 						height: size.height |  | ||||||
| 					}; |  | ||||||
| 					log('image width and height is calculated'); |  | ||||||
| 					return properties; |  | ||||||
| 				})(), |  | ||||||
| 				// folder |  | ||||||
| 				(async () => { |  | ||||||
| 					if (!folderId) { |  | ||||||
| 						return null; |  | ||||||
| 					} |  | ||||||
| 					const driveFolder = await DriveFolder.findOne({ |  | ||||||
| 						_id: folderId, |  | ||||||
| 						user_id: user._id |  | ||||||
| 					}); |  | ||||||
| 					if (!driveFolder) { |  | ||||||
| 						throw 'folder-not-found'; |  | ||||||
| 					} |  | ||||||
| 					return driveFolder; |  | ||||||
| 				})(), |  | ||||||
| 				// usage checker |  | ||||||
| 				(async () => { |  | ||||||
| 					// Calculate drive usage |  | ||||||
| 					const usage = await DriveFile |  | ||||||
| 						.aggregate([ |  | ||||||
| 							{ $match: { 'metadata.user_id': user._id } }, |  | ||||||
| 							{ |  | ||||||
| 								$project: { |  | ||||||
| 									length: true |  | ||||||
| 								} |  | ||||||
| 							}, |  | ||||||
| 							{ |  | ||||||
| 								$group: { |  | ||||||
| 									_id: null, |  | ||||||
| 									usage: { $sum: '$length' } |  | ||||||
| 								} |  | ||||||
| 							} |  | ||||||
| 						]) |  | ||||||
| 						.then((aggregates: any[]) => { |  | ||||||
| 							if (aggregates.length > 0) { |  | ||||||
| 								return aggregates[0].usage; |  | ||||||
| 							} |  | ||||||
| 							return 0; |  | ||||||
| 						}); |  | ||||||
|  |  | ||||||
| 					log(`drive usage is ${usage}`); |  | ||||||
|  |  | ||||||
| 					// If usage limit exceeded |  | ||||||
| 					if (usage + size > user.drive_capacity) { |  | ||||||
| 						throw 'no-free-space'; |  | ||||||
| 					} |  | ||||||
| 				})() |  | ||||||
| 			]); |  | ||||||
|  |  | ||||||
|  | 	// Calculate hash, get content type and get file size | ||||||
|  | 	const [hash, [mime, ext], size] = await Promise.all([ | ||||||
|  | 		// hash | ||||||
|  | 		((): Promise<string> => new Promise((res, rej) => { | ||||||
| 			const readable = fs.createReadStream(path); | 			const readable = fs.createReadStream(path); | ||||||
|  | 			const hash = crypto.createHash('md5'); | ||||||
| 			return addToGridFS(detectedName, readable, mime, { | 			const chunks = []; | ||||||
| 				user_id: user._id, | 			readable | ||||||
| 				folder_id: folder !== null ? folder._id : null, | 				.on('error', rej) | ||||||
| 				comment: comment, | 				.pipe(hash) | ||||||
| 				properties: properties | 				.on('error', rej) | ||||||
|  | 				.on('data', (chunk) => chunks.push(chunk)) | ||||||
|  | 				.on('end', () => { | ||||||
|  | 					const buffer = Buffer.concat(chunks); | ||||||
|  | 					res(buffer.toString('hex')); | ||||||
|  | 				}); | ||||||
|  | 		}))(), | ||||||
|  | 		// mime | ||||||
|  | 		((): Promise<[string, string | null]> => new Promise((res, rej) => { | ||||||
|  | 			const readable = fs.createReadStream(path); | ||||||
|  | 			readable | ||||||
|  | 				.on('error', rej) | ||||||
|  | 				.once('data', (buffer: Buffer) => { | ||||||
|  | 					readable.destroy(); | ||||||
|  | 					const type = fileType(buffer); | ||||||
|  | 					if (!type) { | ||||||
|  | 						return res(['application/octet-stream', null]); | ||||||
|  | 					} | ||||||
|  | 					return res([type.mime, type.ext]); | ||||||
|  | 				}); | ||||||
|  | 		}))(), | ||||||
|  | 		// size | ||||||
|  | 		((): Promise<number> => new Promise((res, rej) => { | ||||||
|  | 			fs.stat(path, (err, stats) => { | ||||||
|  | 				if (err) return rej(err); | ||||||
|  | 				res(stats.size); | ||||||
| 			}); | 			}); | ||||||
| 		}) | 		}))() | ||||||
|  | 	]); | ||||||
|  |  | ||||||
|  | 	log(`hash: ${hash}, mime: ${mime}, ext: ${ext}, size: ${size}`); | ||||||
|  |  | ||||||
|  | 	// detect name | ||||||
|  | 	const detectedName: string = name || (ext ? `untitled.${ext}` : 'untitled'); | ||||||
|  |  | ||||||
|  | 	if (!force) { | ||||||
|  | 		// Check if there is a file with the same hash | ||||||
|  | 		const much = await DriveFile.findOne({ | ||||||
|  | 			md5: hash, | ||||||
|  | 			'metadata.user_id': user._id | ||||||
|  | 		}); | ||||||
|  |  | ||||||
|  | 		if (much !== null) { | ||||||
|  | 			log('file with same hash is found'); | ||||||
|  | 			return much; | ||||||
|  | 		} else { | ||||||
|  | 			log('file with same hash is not found'); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	const [properties, folder] = await Promise.all([ | ||||||
|  | 		// properties | ||||||
|  | 		(async () => { | ||||||
|  | 			if (!/^image\/.*$/.test(mime)) { | ||||||
|  | 				return null; | ||||||
|  | 			} | ||||||
|  | 			// If the file is an image, calculate width and height to save in property | ||||||
|  | 			const g = gm(fs.createReadStream(path), name); | ||||||
|  | 			const size = await prominence(g).size(); | ||||||
|  | 			const properties = { | ||||||
|  | 				width: size.width, | ||||||
|  | 				height: size.height | ||||||
|  | 			}; | ||||||
|  | 			log('image width and height is calculated'); | ||||||
|  | 			return properties; | ||||||
|  | 		})(), | ||||||
|  | 		// folder | ||||||
|  | 		(async () => { | ||||||
|  | 			if (!folderId) { | ||||||
|  | 				return null; | ||||||
|  | 			} | ||||||
|  | 			const driveFolder = await DriveFolder.findOne({ | ||||||
|  | 				_id: folderId, | ||||||
|  | 				user_id: user._id | ||||||
|  | 			}); | ||||||
|  | 			if (!driveFolder) { | ||||||
|  | 				throw 'folder-not-found'; | ||||||
|  | 			} | ||||||
|  | 			return driveFolder; | ||||||
|  | 		})(), | ||||||
|  | 		// usage checker | ||||||
|  | 		(async () => { | ||||||
|  | 			// Calculate drive usage | ||||||
|  | 			const usage = await DriveFile | ||||||
|  | 				.aggregate([ | ||||||
|  | 					{ $match: { 'metadata.user_id': user._id } }, | ||||||
|  | 					{ | ||||||
|  | 						$project: { | ||||||
|  | 							length: true | ||||||
|  | 						} | ||||||
|  | 					}, | ||||||
|  | 					{ | ||||||
|  | 						$group: { | ||||||
|  | 							_id: null, | ||||||
|  | 							usage: { $sum: '$length' } | ||||||
|  | 						} | ||||||
|  | 					} | ||||||
|  | 				]) | ||||||
|  | 				.then((aggregates: any[]) => { | ||||||
|  | 					if (aggregates.length > 0) { | ||||||
|  | 						return aggregates[0].usage; | ||||||
|  | 					} | ||||||
|  | 					return 0; | ||||||
|  | 				}); | ||||||
|  |  | ||||||
|  | 			log(`drive usage is ${usage}`); | ||||||
|  |  | ||||||
|  | 			// If usage limit exceeded | ||||||
|  | 			if (usage + size > user.drive_capacity) { | ||||||
|  | 				throw 'no-free-space'; | ||||||
|  | 			} | ||||||
|  | 		})() | ||||||
|  | 	]); | ||||||
|  |  | ||||||
|  | 	const readable = fs.createReadStream(path); | ||||||
|  |  | ||||||
|  | 	return addToGridFS(detectedName, readable, mime, { | ||||||
|  | 		user_id: user._id, | ||||||
|  | 		folder_id: folder !== null ? folder._id : null, | ||||||
|  | 		comment: comment, | ||||||
|  | 		properties: properties | ||||||
|  | 	}); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Add file to drive | ||||||
|  |  * | ||||||
|  |  * @param user User who wish to add file | ||||||
|  |  * @param file File path or readableStream | ||||||
|  |  * @param comment Comment | ||||||
|  |  * @param type File type | ||||||
|  |  * @param folderId Folder ID | ||||||
|  |  * @param force If set to true, forcibly upload the file even if there is a file with the same hash. | ||||||
|  |  * @return Object that represents added file | ||||||
|  |  */ | ||||||
|  | export default (user: any, file: string | stream.Readable, ...args) => new Promise<any>((resolve, reject) => { | ||||||
|  | 	addFile(user, file, ...args) | ||||||
| 		.then(file => { | 		.then(file => { | ||||||
| 			log(`drive file has been created ${file._id}`); | 			log(`drive file has been created ${file._id}`); | ||||||
| 			resolve(file); | 			resolve(file); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 otofune
					otofune