gridfs.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. // crowi-fileupload-gridFS
  2. module.exports = function(crowi) {
  3. 'use strict';
  4. const debug = require('debug')('growi:service:fileUploaderGridfs');
  5. const mongoose = require('mongoose');
  6. const path = require('path');
  7. const fs = require('fs');
  8. const lib = {};
  9. // instantiate mongoose-gridfs
  10. const gridfs = require('mongoose-gridfs')({
  11. collection: 'attachmentFiles',
  12. model: 'AttachmentFile',
  13. mongooseConnection: mongoose.connection
  14. });
  15. // obtain a model
  16. const AttachmentFile = gridfs.model;
  17. const Chunks = mongoose.model('Chunks', gridfs.schema, 'attachmentFiles.chunks');
  18. // delete a file
  19. lib.deleteFile = async function(fileId, filePath) {
  20. debug('File deletion: ' + fileId);
  21. const file = await getFile(filePath);
  22. const id = file.id;
  23. AttachmentFile.unlinkById(id, function(error, unlinkedAttachment) {
  24. if (error) {
  25. throw new Error(error);
  26. }
  27. });
  28. clearCache(fileId);
  29. };
  30. const clearCache = (fileId) => {
  31. const cacheFile = createCacheFileName(fileId);
  32. const stats = fs.statSync(crowi.cacheDir);
  33. if (stats.isFile(`attachment-${fileId}`)) {
  34. fs.unlink(cacheFile, (err) => {
  35. if (err) {
  36. throw new Error('fail to delete cache file', err);
  37. }
  38. });
  39. }
  40. };
  41. /**
  42. * get size of data uploaded files using (Promise wrapper)
  43. */
  44. const getCollectionSize = () => {
  45. return new Promise((resolve, reject) => {
  46. Chunks.collection.stats((err, data) => {
  47. if (err) {
  48. reject(err);
  49. }
  50. resolve(data.size);
  51. });
  52. });
  53. };
  54. /**
  55. * chech storage for fileUpload reaches MONGO_GRIDFS_TOTAL_LIMIT (for gridfs)
  56. */
  57. lib.checkCapacity = async(uploadFileSize) => {
  58. // skip checking if env var is undefined
  59. if (process.env.MONGO_GRIDFS_TOTAL_LIMIT == null) {
  60. return true;
  61. }
  62. const usingFilesSize = await getCollectionSize();
  63. return (+process.env.MONGO_GRIDFS_TOTAL_LIMIT > usingFilesSize + +uploadFileSize);
  64. };
  65. lib.uploadFile = async function(filePath, contentType, fileStream, options) {
  66. debug('File uploading: ' + filePath);
  67. await writeFile(filePath, contentType, fileStream);
  68. };
  69. /**
  70. * write file to MongoDB with GridFS (Promise wrapper)
  71. */
  72. const writeFile = (filePath, contentType, fileStream) => {
  73. return new Promise((resolve, reject) => {
  74. AttachmentFile.write({
  75. filename: filePath,
  76. contentType: contentType
  77. }, fileStream,
  78. function(error, createdFile) {
  79. if (error) {
  80. reject(error);
  81. }
  82. resolve();
  83. });
  84. });
  85. };
  86. lib.getFileData = async function(filePath) {
  87. const file = await getFile(filePath);
  88. const id = file._id;
  89. const contentType = file.contentType;
  90. const data = await readFileData(id);
  91. return {
  92. data,
  93. contentType
  94. };
  95. };
  96. /**
  97. * get file from MongoDB (Promise wrapper)
  98. */
  99. const getFile = (filePath) => {
  100. return new Promise((resolve, reject) => {
  101. AttachmentFile.findOne({
  102. filename: filePath
  103. }, function(err, file) {
  104. if (err) {
  105. reject(err);
  106. }
  107. resolve(file);
  108. });
  109. });
  110. };
  111. /**
  112. * read File in MongoDB (Promise wrapper)
  113. */
  114. const readFileData = (id) => {
  115. return new Promise((resolve, reject) => {
  116. let buf;
  117. const stream = AttachmentFile.readById(id);
  118. stream.on('error', function(error) {
  119. reject(error);
  120. });
  121. stream.on('data', function(data) {
  122. if (buf) {
  123. buf = Buffer.concat([buf, data]);
  124. }
  125. else {
  126. buf = data;
  127. }
  128. });
  129. stream.on('close', function() {
  130. debug('GridFS readstream closed');
  131. resolve(buf);
  132. });
  133. });
  134. };
  135. lib.findDeliveryFile = async function(fileId, filePath) {
  136. const cacheFile = createCacheFileName(fileId);
  137. debug('Load attachement file into local cache file', cacheFile);
  138. const fileStream = fs.createWriteStream(cacheFile);
  139. const file = await getFile(filePath);
  140. const id = file.id;
  141. const buf = await readFileData(id);
  142. await writeCacheFile(fileStream, buf);
  143. return cacheFile;
  144. };
  145. const createCacheFileName = (fileId) => {
  146. return path.join(crowi.cacheDir, `attachment-${fileId}`);
  147. };
  148. /**
  149. * write cache file (Promise wrapper)
  150. */
  151. const writeCacheFile = (fileStream, data) => {
  152. return new Promise((resolve, reject) => {
  153. fileStream.write(data);
  154. resolve();
  155. });
  156. };
  157. lib.generateUrl = function(filePath) {
  158. return `/${filePath}`;
  159. };
  160. return lib;
  161. };