gcs.js 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. const logger = require('@alias/logger')('growi:service:fileUploaderAws');
  2. const urljoin = require('url-join');
  3. const { Storage } = require('@google-cloud/storage');
  4. let _instance;
  5. module.exports = function(crowi) {
  6. const Uploader = require('./uploader');
  7. const { configManager } = crowi;
  8. const lib = new Uploader(crowi);
  9. function getGcsBucket() {
  10. return configManager.getConfig('crowi', 'gcs:bucket');
  11. }
  12. function getGcsInstance() {
  13. if (_instance == null) {
  14. const keyFilename = configManager.getConfig('crowi', 'gcs:apiKeyJsonPath');
  15. // see https://googleapis.dev/nodejs/storage/latest/Storage.html
  16. _instance = new Storage({ keyFilename });
  17. }
  18. return _instance;
  19. }
  20. function getFilePathOnStorage(attachment) {
  21. const namespace = configManager.getConfig('crowi', 'gcs:uploadNamespace');
  22. // const namespace = null;
  23. const dirName = (attachment.page != null)
  24. ? 'attachment'
  25. : 'user';
  26. const filePath = urljoin(namespace || '', dirName, attachment.fileName);
  27. return filePath;
  28. }
  29. /**
  30. * check file existence
  31. * @param {File} file https://googleapis.dev/nodejs/storage/latest/File.html
  32. */
  33. async function isFileExists(file) {
  34. // check file exists
  35. const res = await file.exists();
  36. return res[0];
  37. }
  38. lib.isValidUploadSettings = function() {
  39. return this.configManager.getConfig('crowi', 'gcs:apiKeyJsonPath') != null
  40. && this.configManager.getConfig('crowi', 'gcs:bucket') != null;
  41. };
  42. lib.deleteFile = async function(attachment) {
  43. const filePath = getFilePathOnStorage(attachment);
  44. return lib.deleteFileByFilePath(filePath);
  45. };
  46. lib.deleteFileByFilePath = async function(filePath) {
  47. if (!this.getIsUploadable()) {
  48. throw new Error('GCS is not configured.');
  49. }
  50. const gcs = getGcsInstance();
  51. const myBucket = gcs.bucket(getGcsBucket());
  52. const file = myBucket.file(filePath);
  53. // check file exists
  54. const isExists = await isFileExists(file);
  55. if (!isExists) {
  56. logger.warn(`Any object that relate to the Attachment (${filePath}) does not exist in GCS`);
  57. return;
  58. }
  59. return file.delete();
  60. };
  61. lib.uploadFile = function(fileStream, attachment) {
  62. if (!this.getIsUploadable()) {
  63. throw new Error('GCS is not configured.');
  64. }
  65. logger.debug(`File uploading: fileName=${attachment.fileName}`);
  66. const gcs = getGcsInstance();
  67. const myBucket = gcs.bucket(getGcsBucket());
  68. const filePath = getFilePathOnStorage(attachment);
  69. const options = {
  70. destination: filePath,
  71. };
  72. return myBucket.upload(fileStream.path, options);
  73. };
  74. /**
  75. * Find data substance
  76. *
  77. * @param {Attachment} attachment
  78. * @return {stream.Readable} readable stream
  79. */
  80. lib.findDeliveryFile = async function(attachment) {
  81. if (!this.getIsReadable()) {
  82. throw new Error('GCS is not configured.');
  83. }
  84. const gcs = getGcsInstance();
  85. const myBucket = gcs.bucket(getGcsBucket());
  86. const filePath = getFilePathOnStorage(attachment);
  87. const file = myBucket.file(filePath);
  88. // check file exists
  89. const isExists = await isFileExists(file);
  90. if (!isExists) {
  91. throw new Error(`Any object that relate to the Attachment (${filePath}) does not exist in GCS`);
  92. }
  93. let stream;
  94. try {
  95. stream = file.createReadStream();
  96. }
  97. catch (err) {
  98. logger.error(err);
  99. throw new Error(`Coudn't get file from AWS for the Attachment (${attachment._id.toString()})`);
  100. }
  101. // return stream.Readable
  102. return stream;
  103. };
  104. /**
  105. * check the file size limit
  106. *
  107. * In detail, the followings are checked.
  108. * - per-file size limit (specified by MAX_FILE_SIZE)
  109. */
  110. lib.checkLimit = async(uploadFileSize) => {
  111. const maxFileSize = crowi.configManager.getConfig('crowi', 'app:maxFileSize');
  112. const gcsTotalLimit = crowi.configManager.getConfig('crowi', 'app:fileUploadTotalLimit');
  113. return lib.doCheckLimit(uploadFileSize, maxFileSize, gcsTotalLimit);
  114. };
  115. return lib;
  116. };