Просмотр исходного кода

Merge pull request #10774 from growilabs/feat/158232-167085-content-disposition-headers-logic

feat: Apply Content-Disposition header based on MIME type settings and download opts
Yuki Takei 1 месяц назад
Родитель
Сommit
f1d68e3123

+ 4 - 1
apps/app/src/server/routes/attachment/get.ts

@@ -132,7 +132,10 @@ const respondForRelayMode = async (
   opts?: RespondOptions,
 ): Promise<void> => {
   // apply content-* headers before response
-  const contentHeaders = createContentHeaders(attachment);
+  const isDownload = opts?.download ?? false;
+  const contentHeaders = createContentHeaders(attachment, {
+    forceAttachment: isDownload,
+  });
   applyHeaders(res, contentHeaders);
 
   try {

+ 2 - 1
apps/app/src/server/service/file-uploader/aws/index.ts

@@ -286,7 +286,8 @@ class AwsFileUploader extends AbstractFileUploader {
 
     // issue signed url (default: expires 120 seconds)
     // https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#getSignedUrl-property
-    const contentHeaders = createContentHeaders(attachment);
+    const isDownload = opts?.download ?? false;
+    const contentHeaders = createContentHeaders(attachment, { forceAttachment: isDownload });
     const params: GetObjectCommandInput = {
       Bucket: getS3Bucket(),
       Key: filePath,

+ 2 - 1
apps/app/src/server/service/file-uploader/azure.ts

@@ -274,7 +274,8 @@ class AzureFileUploader extends AbstractFileUploader {
       const expiresOn = new Date(now + lifetimeSecForTemporaryUrl * 1000);
       const userDelegationKey = await blobServiceClient.getUserDelegationKey(startsOn, expiresOn);
 
-      const contentHeaders = createContentHeaders(attachment);
+      const isDownload = opts?.download ?? false;
+      const contentHeaders = createContentHeaders(attachment, { forceAttachment: isDownload });
 
       // https://github.com/Azure/azure-sdk-for-js/blob/d4d55f73/sdk/storage/storage-blob/src/ContainerSASPermissions.ts#L24
       // r:read, a:add, c:create, w:write, d:delete, l:list

+ 2 - 1
apps/app/src/server/service/file-uploader/gcs/index.ts

@@ -217,7 +217,8 @@ class GcsFileUploader extends AbstractFileUploader {
 
     // issue signed url (default: expires 120 seconds)
     // https://cloud.google.com/storage/docs/access-control/signed-urls
-    const contentHeaders = createContentHeaders(attachment);
+    const isDownload = opts?.download ?? false;
+    const contentHeaders = createContentHeaders(attachment, { forceAttachment: isDownload });
     const [signedUrl] = await file.getSignedUrl({
       action: 'read',
       expires: Date.now() + lifetimeSecForTemporaryUrl * 1000,

+ 2 - 1
apps/app/src/server/service/file-uploader/local.ts

@@ -249,7 +249,8 @@ module.exports = function(crowi: Crowi) {
     const internalPathRoot = configManager.getConfig('fileUpload:local:internalRedirectPath');
     const internalPath = urljoin(internalPathRoot, relativePath);
 
-    const contentHeaders = createContentHeaders(attachment);
+    const isDownload = opts?.download ?? false;
+    const contentHeaders = createContentHeaders(attachment, { forceAttachment: isDownload });
     applyHeaders(res, [
       ...toExpressHttpHeaders(contentHeaders),
       { field: 'X-Accel-Redirect', value: internalPath },

+ 4 - 2
apps/app/src/server/service/file-uploader/utils/headers.ts

@@ -35,7 +35,7 @@ export const determineDisposition = (
  * Factory function to generate content headers.
  * This approach avoids creating a class instance for each call, improving memory efficiency.
  */
-export const createContentHeaders = (attachment: IAttachmentDocument): ContentHeader[] => {
+export const createContentHeaders = (attachment: IAttachmentDocument, opts?: { forceAttachment?: boolean }): ContentHeader[] => {
   const headers: ContentHeader[] = [];
 
   // Content-Type
@@ -52,7 +52,9 @@ export const createContentHeaders = (attachment: IAttachmentDocument): ContentHe
   });
 
   // Content-Disposition
-  const disposition = determineDisposition(attachment.fileFormat);
+  const disposition = opts?.forceAttachment
+    ? 'attachment'
+    : determineDisposition(attachment.fileFormat);
   headers.push({
     field: 'Content-Disposition',
     value: `${disposition};filename*=UTF-8''${encodeURIComponent(attachment.originalName)}`,