aws.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. const logger = require('@alias/logger')('growi:service:fileUploaderAws');
  2. const urljoin = require('url-join');
  3. const aws = require('aws-sdk');
  4. module.exports = function(crowi) {
  5. const Uploader = require('./uploader');
  6. const { configManager } = crowi;
  7. const lib = new Uploader(crowi);
  8. function getAwsConfig() {
  9. return {
  10. accessKeyId: configManager.getConfig('crowi', 'aws:accessKeyId'),
  11. secretAccessKey: configManager.getConfig('crowi', 'aws:secretAccessKey'),
  12. region: configManager.getConfig('crowi', 'aws:region'),
  13. bucket: configManager.getConfig('crowi', 'aws:bucket'),
  14. customEndpoint: configManager.getConfig('crowi', 'aws:customEndpoint'),
  15. };
  16. }
  17. function S3Factory(isUploadable) {
  18. const awsConfig = getAwsConfig();
  19. if (!isUploadable) {
  20. throw new Error('AWS is not configured.');
  21. }
  22. aws.config.update({
  23. accessKeyId: awsConfig.accessKeyId,
  24. secretAccessKey: awsConfig.secretAccessKey,
  25. region: awsConfig.region,
  26. s3ForcePathStyle: awsConfig.customEndpoint ? true : undefined,
  27. });
  28. // undefined & null & '' => default endpoint (genuine S3)
  29. return new aws.S3({ endpoint: awsConfig.customEndpoint || undefined });
  30. }
  31. function getFilePathOnStorage(attachment) {
  32. if (attachment.filePath != null) { // backward compatibility for v3.3.x or below
  33. return attachment.filePath;
  34. }
  35. const dirName = (attachment.page != null)
  36. ? 'attachment'
  37. : 'user';
  38. const filePath = urljoin(dirName, attachment.fileName);
  39. return filePath;
  40. }
  41. lib.isValidUploadSettings = function() {
  42. return this.configManager.getConfig('crowi', 'aws:accessKeyId') != null
  43. && this.configManager.getConfig('crowi', 'aws:secretAccessKey') != null
  44. && (
  45. this.configManager.getConfig('crowi', 'aws:region') != null
  46. || this.configManager.getConfig('crowi', 'aws:customEndpoint') != null
  47. )
  48. && this.configManager.getConfig('crowi', 'aws:bucket') != null;
  49. };
  50. lib.deleteFile = async function(attachment) {
  51. const filePath = getFilePathOnStorage(attachment);
  52. return lib.deleteFileByFilePath(filePath);
  53. };
  54. lib.deleteFileByFilePath = async function(filePath) {
  55. const s3 = S3Factory(this.getIsUploadable());
  56. const awsConfig = getAwsConfig();
  57. const params = {
  58. Bucket: awsConfig.bucket,
  59. Key: filePath,
  60. };
  61. // TODO: ensure not to throw error even when the file does not exist
  62. return s3.deleteObject(params).promise();
  63. };
  64. lib.uploadFile = function(fileStream, attachment) {
  65. logger.debug(`File uploading: fileName=${attachment.fileName}`);
  66. const s3 = S3Factory(this.getIsUploadable());
  67. const awsConfig = getAwsConfig();
  68. const filePath = getFilePathOnStorage(attachment);
  69. const params = {
  70. Bucket: awsConfig.bucket,
  71. ContentType: attachment.fileFormat,
  72. Key: filePath,
  73. Body: fileStream,
  74. ACL: 'public-read',
  75. };
  76. return s3.upload(params).promise();
  77. };
  78. /**
  79. * Find data substance
  80. *
  81. * @param {Attachment} attachment
  82. * @return {stream.Readable} readable stream
  83. */
  84. lib.findDeliveryFile = async function(attachment) {
  85. const s3 = S3Factory(this.getIsUploadable());
  86. const awsConfig = getAwsConfig();
  87. const filePath = getFilePathOnStorage(attachment);
  88. let stream;
  89. try {
  90. const params = {
  91. Bucket: awsConfig.bucket,
  92. Key: filePath,
  93. };
  94. stream = s3.getObject(params).createReadStream();
  95. }
  96. catch (err) {
  97. logger.error(err);
  98. throw new Error(`Coudn't get file from AWS for the Attachment (${attachment._id.toString()})`);
  99. }
  100. // return stream.Readable
  101. return stream;
  102. };
  103. /**
  104. * check the file size limit
  105. *
  106. * In detail, the followings are checked.
  107. * - per-file size limit (specified by MAX_FILE_SIZE)
  108. */
  109. lib.checkLimit = async(uploadFileSize) => {
  110. const maxFileSize = crowi.configManager.getConfig('crowi', 'app:maxFileSize');
  111. const totalLimit = crowi.configManager.getConfig('crowi', 'app:fileUploadTotalLimit');
  112. return lib.doCheckLimit(uploadFileSize, maxFileSize, totalLimit);
  113. };
  114. return lib;
  115. };