@strapi/upload 5.47.1 → 5.48.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/dist/admin/components/EditAssetDialog/EditAssetContent.js +12 -2
  2. package/dist/admin/components/EditAssetDialog/EditAssetContent.js.map +1 -1
  3. package/dist/admin/components/EditAssetDialog/EditAssetContent.mjs +12 -2
  4. package/dist/admin/components/EditAssetDialog/EditAssetContent.mjs.map +1 -1
  5. package/dist/admin/components/UploadAssetDialog/UploadAssetDialog.js +1 -0
  6. package/dist/admin/components/UploadAssetDialog/UploadAssetDialog.js.map +1 -1
  7. package/dist/admin/components/UploadAssetDialog/UploadAssetDialog.mjs +1 -0
  8. package/dist/admin/components/UploadAssetDialog/UploadAssetDialog.mjs.map +1 -1
  9. package/dist/admin/future/components/Drawer.js +7 -8
  10. package/dist/admin/future/components/Drawer.js.map +1 -1
  11. package/dist/admin/future/components/Drawer.mjs +7 -8
  12. package/dist/admin/future/components/Drawer.mjs.map +1 -1
  13. package/dist/admin/future/components/UploadProgressDialog.js +33 -29
  14. package/dist/admin/future/components/UploadProgressDialog.js.map +1 -1
  15. package/dist/admin/future/components/UploadProgressDialog.mjs +36 -32
  16. package/dist/admin/future/components/UploadProgressDialog.mjs.map +1 -1
  17. package/dist/admin/future/pages/Assets/AssetsPage.js +2 -2
  18. package/dist/admin/future/pages/Assets/AssetsPage.js.map +1 -1
  19. package/dist/admin/future/pages/Assets/AssetsPage.mjs +3 -3
  20. package/dist/admin/future/pages/Assets/AssetsPage.mjs.map +1 -1
  21. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js +626 -169
  22. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js.map +1 -1
  23. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs +630 -175
  24. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs.map +1 -1
  25. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetPreview.js +25 -5
  26. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetPreview.js.map +1 -1
  27. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetPreview.mjs +25 -5
  28. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetPreview.mjs.map +1 -1
  29. package/dist/admin/future/services/api.js +124 -200
  30. package/dist/admin/future/services/api.js.map +1 -1
  31. package/dist/admin/future/services/api.mjs +124 -200
  32. package/dist/admin/future/services/api.mjs.map +1 -1
  33. package/dist/admin/future/services/assets.js +57 -1
  34. package/dist/admin/future/services/assets.js.map +1 -1
  35. package/dist/admin/future/services/assets.mjs +56 -2
  36. package/dist/admin/future/services/assets.mjs.map +1 -1
  37. package/dist/admin/future/services/settings.js +18 -0
  38. package/dist/admin/future/services/settings.js.map +1 -0
  39. package/dist/admin/future/services/settings.mjs +16 -0
  40. package/dist/admin/future/services/settings.mjs.map +1 -0
  41. package/dist/admin/future/services/uploadFileViaXHR.js +92 -0
  42. package/dist/admin/future/services/uploadFileViaXHR.js.map +1 -0
  43. package/dist/admin/future/services/uploadFileViaXHR.mjs +88 -0
  44. package/dist/admin/future/services/uploadFileViaXHR.mjs.map +1 -0
  45. package/dist/admin/future/store/uploadProgress.js +32 -26
  46. package/dist/admin/future/store/uploadProgress.js.map +1 -1
  47. package/dist/admin/future/store/uploadProgress.mjs +32 -27
  48. package/dist/admin/future/store/uploadProgress.mjs.map +1 -1
  49. package/dist/admin/future/utils/createRafBatcher.js +42 -0
  50. package/dist/admin/future/utils/createRafBatcher.js.map +1 -0
  51. package/dist/admin/future/utils/createRafBatcher.mjs +40 -0
  52. package/dist/admin/future/utils/createRafBatcher.mjs.map +1 -0
  53. package/dist/admin/future/utils/downloadFile.js +19 -0
  54. package/dist/admin/future/utils/downloadFile.js.map +1 -0
  55. package/dist/admin/future/utils/downloadFile.mjs +17 -0
  56. package/dist/admin/future/utils/downloadFile.mjs.map +1 -0
  57. package/dist/admin/hooks/useAssets.js +5 -3
  58. package/dist/admin/hooks/useAssets.js.map +1 -1
  59. package/dist/admin/hooks/useAssets.mjs +5 -3
  60. package/dist/admin/hooks/useAssets.mjs.map +1 -1
  61. package/dist/admin/src/components/EditAssetDialog/EditAssetContent.d.ts +2 -1
  62. package/dist/admin/src/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.d.ts +15 -1
  63. package/dist/admin/src/future/pages/Assets/components/AssetDetails/AssetPreview.d.ts +4 -1
  64. package/dist/admin/src/future/services/api.d.ts +9 -8
  65. package/dist/admin/src/future/services/assets.d.ts +6 -1
  66. package/dist/admin/src/future/services/uploadFileViaXHR.d.ts +34 -0
  67. package/dist/admin/src/future/store/uploadProgress.d.ts +17 -4
  68. package/dist/admin/src/future/utils/createRafBatcher.d.ts +23 -0
  69. package/dist/admin/src/future/utils/downloadFile.d.ts +6 -0
  70. package/dist/admin/translations/en.json.js +21 -0
  71. package/dist/admin/translations/en.json.js.map +1 -1
  72. package/dist/admin/translations/en.json.mjs +21 -0
  73. package/dist/admin/translations/en.json.mjs.map +1 -1
  74. package/dist/server/controllers/admin-upload.js +69 -118
  75. package/dist/server/controllers/admin-upload.js.map +1 -1
  76. package/dist/server/controllers/admin-upload.mjs +69 -118
  77. package/dist/server/controllers/admin-upload.mjs.map +1 -1
  78. package/dist/server/controllers/content-api.js +12 -0
  79. package/dist/server/controllers/content-api.js.map +1 -1
  80. package/dist/server/controllers/content-api.mjs +12 -0
  81. package/dist/server/controllers/content-api.mjs.map +1 -1
  82. package/dist/server/routes/admin.js +2 -2
  83. package/dist/server/routes/admin.js.map +1 -1
  84. package/dist/server/routes/admin.mjs +2 -2
  85. package/dist/server/routes/admin.mjs.map +1 -1
  86. package/dist/server/routes/content-api.js +15 -0
  87. package/dist/server/routes/content-api.js.map +1 -1
  88. package/dist/server/routes/content-api.mjs +15 -0
  89. package/dist/server/routes/content-api.mjs.map +1 -1
  90. package/dist/server/routes/validation/upload.js +28 -0
  91. package/dist/server/routes/validation/upload.js.map +1 -1
  92. package/dist/server/routes/validation/upload.mjs +28 -0
  93. package/dist/server/routes/validation/upload.mjs.map +1 -1
  94. package/dist/server/services/image-manipulation.js +16 -8
  95. package/dist/server/services/image-manipulation.js.map +1 -1
  96. package/dist/server/services/image-manipulation.mjs +16 -8
  97. package/dist/server/services/image-manipulation.mjs.map +1 -1
  98. package/dist/server/services/upload.js +90 -1
  99. package/dist/server/services/upload.js.map +1 -1
  100. package/dist/server/services/upload.mjs +91 -2
  101. package/dist/server/services/upload.mjs.map +1 -1
  102. package/dist/server/src/controllers/admin-upload.d.ts +6 -8
  103. package/dist/server/src/controllers/admin-upload.d.ts.map +1 -1
  104. package/dist/server/src/controllers/content-api.d.ts +1 -0
  105. package/dist/server/src/controllers/content-api.d.ts.map +1 -1
  106. package/dist/server/src/controllers/index.d.ts +2 -1
  107. package/dist/server/src/controllers/index.d.ts.map +1 -1
  108. package/dist/server/src/index.d.ts +6 -1
  109. package/dist/server/src/index.d.ts.map +1 -1
  110. package/dist/server/src/routes/content-api.d.ts.map +1 -1
  111. package/dist/server/src/routes/validation/upload.d.ts +45 -0
  112. package/dist/server/src/routes/validation/upload.d.ts.map +1 -1
  113. package/dist/server/src/services/image-manipulation.d.ts +5 -0
  114. package/dist/server/src/services/image-manipulation.d.ts.map +1 -1
  115. package/dist/server/src/services/index.d.ts +4 -0
  116. package/dist/server/src/services/index.d.ts.map +1 -1
  117. package/dist/server/src/services/upload.d.ts +5 -0
  118. package/dist/server/src/services/upload.d.ts.map +1 -1
  119. package/dist/server/src/types.d.ts +2 -2
  120. package/dist/server/src/types.d.ts.map +1 -1
  121. package/dist/shared/contracts/files.d.ts +19 -2
  122. package/dist/shared/contracts/files.d.ts.map +1 -1
  123. package/package.json +8 -8
@@ -66,14 +66,18 @@ const resizeFileTo = async (file, options, { name, hash })=>{
66
66
  const filePath = file.tmpWorkingDirectory ? path.join(file.tmpWorkingDirectory, hash) : hash;
67
67
  let newInfo;
68
68
  if (!file.filepath) {
69
- const transform = sharp().resize(options).on('info', (info)=>{
69
+ const transform = sharp({
70
+ animated: true
71
+ }).resize(options).on('info', (info)=>{
70
72
  newInfo = info;
71
73
  });
72
74
  await writeStreamToFile(file.getStream().pipe(transform), filePath);
73
75
  } else {
74
- newInfo = await sharp(file.filepath).resize(options).toFile(filePath);
76
+ newInfo = await sharp(file.filepath, {
77
+ animated: true
78
+ }).resize(options).toFile(filePath);
75
79
  }
76
- const { width, height, size } = newInfo ?? {};
80
+ const { width, height, size, pageHeight } = newInfo ?? {};
77
81
  const newFile = {
78
82
  name,
79
83
  hash,
@@ -85,7 +89,7 @@ const resizeFileTo = async (file, options, { name, hash })=>{
85
89
  };
86
90
  Object.assign(newFile, {
87
91
  width,
88
- height,
92
+ height: pageHeight ?? height,
89
93
  size: size ? bytesToKbytes(size) : 0,
90
94
  sizeInBytes: size
91
95
  });
@@ -111,9 +115,13 @@ const generateThumbnail = async (file)=>{
111
115
  if ((sizeOptimization || autoOrientation) && isOptimizableFormat(format)) {
112
116
  let transformer;
113
117
  if (!file.filepath) {
114
- transformer = sharp();
118
+ transformer = sharp({
119
+ animated: true
120
+ });
115
121
  } else {
116
- transformer = sharp(file.filepath);
122
+ transformer = sharp(file.filepath, {
123
+ animated: true
124
+ });
117
125
  }
118
126
  // reduce image quality
119
127
  transformer[format]({
@@ -133,7 +141,7 @@ const generateThumbnail = async (file)=>{
133
141
  } else {
134
142
  newInfo = await transformer.toFile(filePath);
135
143
  }
136
- const { width: newWidth, height: newHeight, size: newSize } = newInfo ?? {};
144
+ const { width: newWidth, height: newHeight, size: newSize, pageHeight: newPageHeight } = newInfo ?? {};
137
145
  const newFile = {
138
146
  ...file
139
147
  };
@@ -145,7 +153,7 @@ const generateThumbnail = async (file)=>{
145
153
  }
146
154
  return Object.assign(newFile, {
147
155
  width: newWidth,
148
- height: newHeight,
156
+ height: newPageHeight ?? newHeight,
149
157
  size: newSize ? bytesToKbytes(newSize) : 0,
150
158
  sizeInBytes: newSize
151
159
  });
@@ -1 +1 @@
1
- {"version":3,"file":"image-manipulation.js","sources":["../../../server/src/services/image-manipulation.ts"],"sourcesContent":["import fs from 'fs';\nimport { join } from 'path';\nimport sharp from 'sharp';\nimport crypto from 'crypto';\nimport { strings, file as fileUtils } from '@strapi/utils';\n\nimport { getService } from '../utils';\n\nimport type { UploadableFile } from '../types';\n\ntype Dimensions = {\n width: number | null;\n height: number | null;\n};\n\nconst { bytesToKbytes } = fileUtils;\n\nconst FORMATS_TO_RESIZE = ['jpeg', 'png', 'webp', 'tiff', 'gif'];\nconst FORMATS_TO_PROCESS = ['jpeg', 'png', 'webp', 'tiff', 'svg', 'gif', 'avif'];\nconst FORMATS_TO_OPTIMIZE = ['jpeg', 'png', 'webp', 'tiff', 'avif'];\n\nconst isOptimizableFormat = (\n format: string | undefined\n): format is 'jpeg' | 'png' | 'webp' | 'tiff' | 'avif' =>\n format !== undefined && FORMATS_TO_OPTIMIZE.includes(format);\n\nconst writeStreamToFile = (stream: NodeJS.ReadWriteStream, path: string) =>\n new Promise<void>((resolve, reject) => {\n const writeStream = fs.createWriteStream(path);\n // Reject promise if there is an error with the provided stream\n stream.on('error', reject);\n stream.pipe(writeStream);\n writeStream.on('close', () => resolve());\n writeStream.on('error', reject);\n });\n\nconst getMetadata = (file: UploadableFile): Promise<sharp.Metadata> => {\n if (!file.filepath) {\n return new Promise((resolve, reject) => {\n const pipeline = sharp();\n pipeline.metadata().then(resolve).catch(reject);\n file.getStream().pipe(pipeline);\n });\n }\n\n return sharp(file.filepath).metadata();\n};\n\nconst getDimensions = async (file: UploadableFile): Promise<Dimensions> => {\n const { width = null, height = null } = await getMetadata(file);\n\n return { width, height };\n};\n\nconst THUMBNAIL_RESIZE_OPTIONS = {\n width: 245,\n height: 156,\n fit: 'inside',\n} satisfies sharp.ResizeOptions;\n\nconst resizeFileTo = async (\n file: UploadableFile,\n options: sharp.ResizeOptions,\n {\n name,\n hash,\n }: {\n name: string;\n hash: string;\n }\n) => {\n const filePath = file.tmpWorkingDirectory ? join(file.tmpWorkingDirectory, hash) : hash;\n\n let newInfo;\n if (!file.filepath) {\n const transform = sharp()\n .resize(options)\n .on('info', (info) => {\n newInfo = info;\n });\n\n await writeStreamToFile(file.getStream().pipe(transform), filePath);\n } else {\n newInfo = await sharp(file.filepath).resize(options).toFile(filePath);\n }\n\n const { width, height, size } = newInfo ?? {};\n\n const newFile: UploadableFile = {\n name,\n hash,\n ext: file.ext,\n mime: file.mime,\n filepath: filePath,\n path: file.path || null,\n getStream: () => fs.createReadStream(filePath),\n };\n\n Object.assign(newFile, {\n width,\n height,\n size: size ? bytesToKbytes(size) : 0,\n sizeInBytes: size,\n });\n return newFile;\n};\n\nconst generateThumbnail = async (file: UploadableFile) => {\n if (\n file.width &&\n file.height &&\n (file.width > THUMBNAIL_RESIZE_OPTIONS.width || file.height > THUMBNAIL_RESIZE_OPTIONS.height)\n ) {\n return resizeFileTo(file, THUMBNAIL_RESIZE_OPTIONS, {\n name: `thumbnail_${file.name}`,\n hash: `thumbnail_${file.hash}`,\n });\n }\n\n return null;\n};\n\n/**\n * Optimize image by:\n * - auto orienting image based on EXIF data\n * - reduce image quality\n *\n */\nconst optimize = async (file: UploadableFile) => {\n const { sizeOptimization = false, autoOrientation = false } =\n (await getService('upload').getSettings()) ?? {};\n\n const { format, size } = await getMetadata(file);\n\n if ((sizeOptimization || autoOrientation) && isOptimizableFormat(format)) {\n let transformer;\n if (!file.filepath) {\n transformer = sharp();\n } else {\n transformer = sharp(file.filepath);\n }\n // reduce image quality\n transformer[format]({ quality: sizeOptimization ? 80 : 100 });\n // rotate image based on EXIF data\n if (autoOrientation) {\n transformer.rotate();\n }\n const filePath = file.tmpWorkingDirectory\n ? join(file.tmpWorkingDirectory, `optimized-${file.hash}`)\n : `optimized-${file.hash}`;\n\n let newInfo;\n if (!file.filepath) {\n transformer.on('info', (info) => {\n newInfo = info;\n });\n\n await writeStreamToFile(file.getStream().pipe(transformer), filePath);\n } else {\n newInfo = await transformer.toFile(filePath);\n }\n\n const { width: newWidth, height: newHeight, size: newSize } = newInfo ?? {};\n\n const newFile = { ...file };\n\n newFile.getStream = () => fs.createReadStream(filePath);\n newFile.filepath = filePath;\n\n if (newSize && size && newSize > size) {\n // Ignore optimization if output is bigger than original\n return file;\n }\n\n return Object.assign(newFile, {\n width: newWidth,\n height: newHeight,\n size: newSize ? bytesToKbytes(newSize) : 0,\n sizeInBytes: newSize,\n });\n }\n\n return file;\n};\n\nconst DEFAULT_BREAKPOINTS = {\n large: 1000,\n medium: 750,\n small: 500,\n};\n\nconst getBreakpoints = () =>\n strapi.config.get<Record<string, number>>('plugin::upload.breakpoints', DEFAULT_BREAKPOINTS);\n\nconst generateResponsiveFormats = async (file: UploadableFile) => {\n const { responsiveDimensions = false } = (await getService('upload').getSettings()) ?? {};\n\n if (!responsiveDimensions) return [];\n\n const originalDimensions = await getDimensions(file);\n\n const breakpoints = getBreakpoints();\n const results = [];\n\n for (const key of Object.keys(breakpoints)) {\n const breakpoint = breakpoints[key];\n\n if (breakpointSmallerThan(breakpoint, originalDimensions)) {\n results.push(await generateBreakpoint(key, { file, breakpoint }));\n }\n }\n\n return results;\n};\n\nconst generateBreakpoint = async (\n key: string,\n { file, breakpoint }: { file: UploadableFile; breakpoint: number }\n) => {\n const newFile = await resizeFileTo(\n file,\n {\n width: breakpoint,\n height: breakpoint,\n fit: 'inside',\n },\n {\n name: `${key}_${file.name}`,\n hash: `${key}_${file.hash}`,\n }\n );\n return {\n key,\n file: newFile,\n };\n};\n\nconst breakpointSmallerThan = (breakpoint: number, { width, height }: Dimensions) => {\n return breakpoint < (width ?? 0) || breakpoint < (height ?? 0);\n};\n\n/**\n * Applies a simple image transformation to see if the image is faulty/corrupted.\n */\nconst isFaultyImage = async (file: UploadableFile) => {\n if (!file.filepath) {\n return new Promise((resolve, reject) => {\n const pipeline = sharp();\n pipeline.stats().then(resolve).catch(reject);\n file.getStream().pipe(pipeline);\n });\n }\n\n try {\n await sharp(file.filepath).stats();\n return false;\n } catch (e) {\n return true;\n }\n};\n\nconst isOptimizableImage = async (file: UploadableFile) => {\n let format;\n try {\n const metadata = await getMetadata(file);\n format = metadata.format;\n } catch (e) {\n // throw when the file is not a supported image\n return false;\n }\n return format && FORMATS_TO_OPTIMIZE.includes(format);\n};\n\nconst isResizableImage = async (file: UploadableFile) => {\n let format;\n try {\n const metadata = await getMetadata(file);\n format = metadata.format;\n } catch (e) {\n // throw when the file is not a supported image\n return false;\n }\n return format && FORMATS_TO_RESIZE.includes(format);\n};\n\nconst isImage = async (file: UploadableFile) => {\n let format;\n try {\n const metadata = await getMetadata(file);\n format = metadata.format;\n } catch (e) {\n // throw when the file is not a supported image\n return false;\n }\n return format && FORMATS_TO_PROCESS.includes(format);\n};\n\nconst generateFileName = (name: string) => {\n const randomSuffix = () => crypto.randomBytes(5).toString('hex');\n const baseName = strings.nameToSlug(name, { separator: '_', lowercase: false });\n\n return `${baseName}_${randomSuffix()}`;\n};\n\nexport default {\n isFaultyImage,\n isOptimizableImage,\n isResizableImage,\n isImage,\n getDimensions,\n generateResponsiveFormats,\n generateThumbnail,\n optimize,\n generateFileName,\n};\n"],"names":["bytesToKbytes","fileUtils","FORMATS_TO_RESIZE","FORMATS_TO_PROCESS","FORMATS_TO_OPTIMIZE","isOptimizableFormat","format","undefined","includes","writeStreamToFile","stream","path","Promise","resolve","reject","writeStream","fs","createWriteStream","on","pipe","getMetadata","file","filepath","pipeline","sharp","metadata","then","catch","getStream","getDimensions","width","height","THUMBNAIL_RESIZE_OPTIONS","fit","resizeFileTo","options","name","hash","filePath","tmpWorkingDirectory","join","newInfo","transform","resize","info","toFile","size","newFile","ext","mime","createReadStream","Object","assign","sizeInBytes","generateThumbnail","optimize","sizeOptimization","autoOrientation","getService","getSettings","transformer","quality","rotate","newWidth","newHeight","newSize","DEFAULT_BREAKPOINTS","large","medium","small","getBreakpoints","strapi","config","get","generateResponsiveFormats","responsiveDimensions","originalDimensions","breakpoints","results","key","keys","breakpoint","breakpointSmallerThan","push","generateBreakpoint","isFaultyImage","stats","e","isOptimizableImage","isResizableImage","isImage","generateFileName","randomSuffix","crypto","randomBytes","toString","baseName","strings","nameToSlug","separator","lowercase"],"mappings":"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"}
1
+ {"version":3,"file":"image-manipulation.js","sources":["../../../server/src/services/image-manipulation.ts"],"sourcesContent":["import fs from 'fs';\nimport { join } from 'path';\nimport sharp from 'sharp';\nimport crypto from 'crypto';\nimport { strings, file as fileUtils } from '@strapi/utils';\n\nimport { getService } from '../utils';\n\nimport type { UploadableFile } from '../types';\n\ntype Dimensions = {\n width: number | null;\n height: number | null;\n};\n\n// TODO: remove after upgrading sharp to >=0.34.2 (pageHeight added to OutputInfo types)\ndeclare module 'sharp' {\n interface OutputInfo {\n pageHeight?: number;\n }\n}\n\nconst { bytesToKbytes } = fileUtils;\n\nconst FORMATS_TO_RESIZE = ['jpeg', 'png', 'webp', 'tiff', 'gif'];\nconst FORMATS_TO_PROCESS = ['jpeg', 'png', 'webp', 'tiff', 'svg', 'gif', 'avif'];\nconst FORMATS_TO_OPTIMIZE = ['jpeg', 'png', 'webp', 'tiff', 'avif'];\n\nconst isOptimizableFormat = (\n format: string | undefined\n): format is 'jpeg' | 'png' | 'webp' | 'tiff' | 'avif' =>\n format !== undefined && FORMATS_TO_OPTIMIZE.includes(format);\n\nconst writeStreamToFile = (stream: NodeJS.ReadWriteStream, path: string) =>\n new Promise<void>((resolve, reject) => {\n const writeStream = fs.createWriteStream(path);\n // Reject promise if there is an error with the provided stream\n stream.on('error', reject);\n stream.pipe(writeStream);\n writeStream.on('close', () => resolve());\n writeStream.on('error', reject);\n });\n\nconst getMetadata = (file: UploadableFile): Promise<sharp.Metadata> => {\n if (!file.filepath) {\n return new Promise((resolve, reject) => {\n const pipeline = sharp();\n pipeline.metadata().then(resolve).catch(reject);\n file.getStream().pipe(pipeline);\n });\n }\n\n return sharp(file.filepath).metadata();\n};\n\nconst getDimensions = async (file: UploadableFile): Promise<Dimensions> => {\n const { width = null, height = null } = await getMetadata(file);\n\n return { width, height };\n};\n\nconst THUMBNAIL_RESIZE_OPTIONS = {\n width: 245,\n height: 156,\n fit: 'inside',\n} satisfies sharp.ResizeOptions;\n\nconst resizeFileTo = async (\n file: UploadableFile,\n options: sharp.ResizeOptions,\n {\n name,\n hash,\n }: {\n name: string;\n hash: string;\n }\n) => {\n const filePath = file.tmpWorkingDirectory ? join(file.tmpWorkingDirectory, hash) : hash;\n\n let newInfo;\n if (!file.filepath) {\n const transform = sharp({ animated: true })\n .resize(options)\n .on('info', (info) => {\n newInfo = info;\n });\n\n await writeStreamToFile(file.getStream().pipe(transform), filePath);\n } else {\n newInfo = await sharp(file.filepath, { animated: true }).resize(options).toFile(filePath);\n }\n\n const { width, height, size, pageHeight } = newInfo ?? {};\n\n const newFile: UploadableFile = {\n name,\n hash,\n ext: file.ext,\n mime: file.mime,\n filepath: filePath,\n path: file.path || null,\n getStream: () => fs.createReadStream(filePath),\n };\n\n Object.assign(newFile, {\n width,\n height: pageHeight ?? height,\n size: size ? bytesToKbytes(size) : 0,\n sizeInBytes: size,\n });\n return newFile;\n};\n\nconst generateThumbnail = async (file: UploadableFile) => {\n if (\n file.width &&\n file.height &&\n (file.width > THUMBNAIL_RESIZE_OPTIONS.width || file.height > THUMBNAIL_RESIZE_OPTIONS.height)\n ) {\n return resizeFileTo(file, THUMBNAIL_RESIZE_OPTIONS, {\n name: `thumbnail_${file.name}`,\n hash: `thumbnail_${file.hash}`,\n });\n }\n\n return null;\n};\n\n/**\n * Optimize image by:\n * - auto orienting image based on EXIF data\n * - reduce image quality\n *\n */\nconst optimize = async (file: UploadableFile) => {\n const { sizeOptimization = false, autoOrientation = false } =\n (await getService('upload').getSettings()) ?? {};\n\n const { format, size } = await getMetadata(file);\n\n if ((sizeOptimization || autoOrientation) && isOptimizableFormat(format)) {\n let transformer;\n if (!file.filepath) {\n transformer = sharp({ animated: true });\n } else {\n transformer = sharp(file.filepath, { animated: true });\n }\n // reduce image quality\n transformer[format]({ quality: sizeOptimization ? 80 : 100 });\n // rotate image based on EXIF data\n if (autoOrientation) {\n transformer.rotate();\n }\n const filePath = file.tmpWorkingDirectory\n ? join(file.tmpWorkingDirectory, `optimized-${file.hash}`)\n : `optimized-${file.hash}`;\n\n let newInfo;\n if (!file.filepath) {\n transformer.on('info', (info) => {\n newInfo = info;\n });\n\n await writeStreamToFile(file.getStream().pipe(transformer), filePath);\n } else {\n newInfo = await transformer.toFile(filePath);\n }\n\n const {\n width: newWidth,\n height: newHeight,\n size: newSize,\n pageHeight: newPageHeight,\n } = newInfo ?? {};\n\n const newFile = { ...file };\n\n newFile.getStream = () => fs.createReadStream(filePath);\n newFile.filepath = filePath;\n\n if (newSize && size && newSize > size) {\n // Ignore optimization if output is bigger than original\n return file;\n }\n\n return Object.assign(newFile, {\n width: newWidth,\n height: newPageHeight ?? newHeight,\n size: newSize ? bytesToKbytes(newSize) : 0,\n sizeInBytes: newSize,\n });\n }\n\n return file;\n};\n\nconst DEFAULT_BREAKPOINTS = {\n large: 1000,\n medium: 750,\n small: 500,\n};\n\nconst getBreakpoints = () =>\n strapi.config.get<Record<string, number>>('plugin::upload.breakpoints', DEFAULT_BREAKPOINTS);\n\nconst generateResponsiveFormats = async (file: UploadableFile) => {\n const { responsiveDimensions = false } = (await getService('upload').getSettings()) ?? {};\n\n if (!responsiveDimensions) return [];\n\n const originalDimensions = await getDimensions(file);\n\n const breakpoints = getBreakpoints();\n const results = [];\n\n for (const key of Object.keys(breakpoints)) {\n const breakpoint = breakpoints[key];\n\n if (breakpointSmallerThan(breakpoint, originalDimensions)) {\n results.push(await generateBreakpoint(key, { file, breakpoint }));\n }\n }\n\n return results;\n};\n\nconst generateBreakpoint = async (\n key: string,\n { file, breakpoint }: { file: UploadableFile; breakpoint: number }\n) => {\n const newFile = await resizeFileTo(\n file,\n {\n width: breakpoint,\n height: breakpoint,\n fit: 'inside',\n },\n {\n name: `${key}_${file.name}`,\n hash: `${key}_${file.hash}`,\n }\n );\n return {\n key,\n file: newFile,\n };\n};\n\nconst breakpointSmallerThan = (breakpoint: number, { width, height }: Dimensions) => {\n return breakpoint < (width ?? 0) || breakpoint < (height ?? 0);\n};\n\n/**\n * Applies a simple image transformation to see if the image is faulty/corrupted.\n */\nconst isFaultyImage = async (file: UploadableFile) => {\n if (!file.filepath) {\n return new Promise((resolve, reject) => {\n const pipeline = sharp();\n pipeline.stats().then(resolve).catch(reject);\n file.getStream().pipe(pipeline);\n });\n }\n\n try {\n await sharp(file.filepath).stats();\n return false;\n } catch (e) {\n return true;\n }\n};\n\nconst isOptimizableImage = async (file: UploadableFile) => {\n let format;\n try {\n const metadata = await getMetadata(file);\n format = metadata.format;\n } catch (e) {\n // throw when the file is not a supported image\n return false;\n }\n return format && FORMATS_TO_OPTIMIZE.includes(format);\n};\n\nconst isResizableImage = async (file: UploadableFile) => {\n let format;\n try {\n const metadata = await getMetadata(file);\n format = metadata.format;\n } catch (e) {\n // throw when the file is not a supported image\n return false;\n }\n return format && FORMATS_TO_RESIZE.includes(format);\n};\n\nconst isImage = async (file: UploadableFile) => {\n let format;\n try {\n const metadata = await getMetadata(file);\n format = metadata.format;\n } catch (e) {\n // throw when the file is not a supported image\n return false;\n }\n return format && FORMATS_TO_PROCESS.includes(format);\n};\n\nconst generateFileName = (name: string) => {\n const randomSuffix = () => crypto.randomBytes(5).toString('hex');\n const baseName = strings.nameToSlug(name, { separator: '_', lowercase: false });\n\n return `${baseName}_${randomSuffix()}`;\n};\n\nexport default {\n isFaultyImage,\n isOptimizableImage,\n isResizableImage,\n isImage,\n getDimensions,\n generateResponsiveFormats,\n generateThumbnail,\n optimize,\n generateFileName,\n};\n"],"names":["bytesToKbytes","fileUtils","FORMATS_TO_RESIZE","FORMATS_TO_PROCESS","FORMATS_TO_OPTIMIZE","isOptimizableFormat","format","undefined","includes","writeStreamToFile","stream","path","Promise","resolve","reject","writeStream","fs","createWriteStream","on","pipe","getMetadata","file","filepath","pipeline","sharp","metadata","then","catch","getStream","getDimensions","width","height","THUMBNAIL_RESIZE_OPTIONS","fit","resizeFileTo","options","name","hash","filePath","tmpWorkingDirectory","join","newInfo","transform","animated","resize","info","toFile","size","pageHeight","newFile","ext","mime","createReadStream","Object","assign","sizeInBytes","generateThumbnail","optimize","sizeOptimization","autoOrientation","getService","getSettings","transformer","quality","rotate","newWidth","newHeight","newSize","newPageHeight","DEFAULT_BREAKPOINTS","large","medium","small","getBreakpoints","strapi","config","get","generateResponsiveFormats","responsiveDimensions","originalDimensions","breakpoints","results","key","keys","breakpoint","breakpointSmallerThan","push","generateBreakpoint","isFaultyImage","stats","e","isOptimizableImage","isResizableImage","isImage","generateFileName","randomSuffix","crypto","randomBytes","toString","baseName","strings","nameToSlug","separator","lowercase"],"mappings":"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"}
@@ -64,14 +64,18 @@ const resizeFileTo = async (file, options, { name, hash })=>{
64
64
  const filePath = file.tmpWorkingDirectory ? join(file.tmpWorkingDirectory, hash) : hash;
65
65
  let newInfo;
66
66
  if (!file.filepath) {
67
- const transform = sharp().resize(options).on('info', (info)=>{
67
+ const transform = sharp({
68
+ animated: true
69
+ }).resize(options).on('info', (info)=>{
68
70
  newInfo = info;
69
71
  });
70
72
  await writeStreamToFile(file.getStream().pipe(transform), filePath);
71
73
  } else {
72
- newInfo = await sharp(file.filepath).resize(options).toFile(filePath);
74
+ newInfo = await sharp(file.filepath, {
75
+ animated: true
76
+ }).resize(options).toFile(filePath);
73
77
  }
74
- const { width, height, size } = newInfo ?? {};
78
+ const { width, height, size, pageHeight } = newInfo ?? {};
75
79
  const newFile = {
76
80
  name,
77
81
  hash,
@@ -83,7 +87,7 @@ const resizeFileTo = async (file, options, { name, hash })=>{
83
87
  };
84
88
  Object.assign(newFile, {
85
89
  width,
86
- height,
90
+ height: pageHeight ?? height,
87
91
  size: size ? bytesToKbytes(size) : 0,
88
92
  sizeInBytes: size
89
93
  });
@@ -109,9 +113,13 @@ const generateThumbnail = async (file)=>{
109
113
  if ((sizeOptimization || autoOrientation) && isOptimizableFormat(format)) {
110
114
  let transformer;
111
115
  if (!file.filepath) {
112
- transformer = sharp();
116
+ transformer = sharp({
117
+ animated: true
118
+ });
113
119
  } else {
114
- transformer = sharp(file.filepath);
120
+ transformer = sharp(file.filepath, {
121
+ animated: true
122
+ });
115
123
  }
116
124
  // reduce image quality
117
125
  transformer[format]({
@@ -131,7 +139,7 @@ const generateThumbnail = async (file)=>{
131
139
  } else {
132
140
  newInfo = await transformer.toFile(filePath);
133
141
  }
134
- const { width: newWidth, height: newHeight, size: newSize } = newInfo ?? {};
142
+ const { width: newWidth, height: newHeight, size: newSize, pageHeight: newPageHeight } = newInfo ?? {};
135
143
  const newFile = {
136
144
  ...file
137
145
  };
@@ -143,7 +151,7 @@ const generateThumbnail = async (file)=>{
143
151
  }
144
152
  return Object.assign(newFile, {
145
153
  width: newWidth,
146
- height: newHeight,
154
+ height: newPageHeight ?? newHeight,
147
155
  size: newSize ? bytesToKbytes(newSize) : 0,
148
156
  sizeInBytes: newSize
149
157
  });
@@ -1 +1 @@
1
- {"version":3,"file":"image-manipulation.mjs","sources":["../../../server/src/services/image-manipulation.ts"],"sourcesContent":["import fs from 'fs';\nimport { join } from 'path';\nimport sharp from 'sharp';\nimport crypto from 'crypto';\nimport { strings, file as fileUtils } from '@strapi/utils';\n\nimport { getService } from '../utils';\n\nimport type { UploadableFile } from '../types';\n\ntype Dimensions = {\n width: number | null;\n height: number | null;\n};\n\nconst { bytesToKbytes } = fileUtils;\n\nconst FORMATS_TO_RESIZE = ['jpeg', 'png', 'webp', 'tiff', 'gif'];\nconst FORMATS_TO_PROCESS = ['jpeg', 'png', 'webp', 'tiff', 'svg', 'gif', 'avif'];\nconst FORMATS_TO_OPTIMIZE = ['jpeg', 'png', 'webp', 'tiff', 'avif'];\n\nconst isOptimizableFormat = (\n format: string | undefined\n): format is 'jpeg' | 'png' | 'webp' | 'tiff' | 'avif' =>\n format !== undefined && FORMATS_TO_OPTIMIZE.includes(format);\n\nconst writeStreamToFile = (stream: NodeJS.ReadWriteStream, path: string) =>\n new Promise<void>((resolve, reject) => {\n const writeStream = fs.createWriteStream(path);\n // Reject promise if there is an error with the provided stream\n stream.on('error', reject);\n stream.pipe(writeStream);\n writeStream.on('close', () => resolve());\n writeStream.on('error', reject);\n });\n\nconst getMetadata = (file: UploadableFile): Promise<sharp.Metadata> => {\n if (!file.filepath) {\n return new Promise((resolve, reject) => {\n const pipeline = sharp();\n pipeline.metadata().then(resolve).catch(reject);\n file.getStream().pipe(pipeline);\n });\n }\n\n return sharp(file.filepath).metadata();\n};\n\nconst getDimensions = async (file: UploadableFile): Promise<Dimensions> => {\n const { width = null, height = null } = await getMetadata(file);\n\n return { width, height };\n};\n\nconst THUMBNAIL_RESIZE_OPTIONS = {\n width: 245,\n height: 156,\n fit: 'inside',\n} satisfies sharp.ResizeOptions;\n\nconst resizeFileTo = async (\n file: UploadableFile,\n options: sharp.ResizeOptions,\n {\n name,\n hash,\n }: {\n name: string;\n hash: string;\n }\n) => {\n const filePath = file.tmpWorkingDirectory ? join(file.tmpWorkingDirectory, hash) : hash;\n\n let newInfo;\n if (!file.filepath) {\n const transform = sharp()\n .resize(options)\n .on('info', (info) => {\n newInfo = info;\n });\n\n await writeStreamToFile(file.getStream().pipe(transform), filePath);\n } else {\n newInfo = await sharp(file.filepath).resize(options).toFile(filePath);\n }\n\n const { width, height, size } = newInfo ?? {};\n\n const newFile: UploadableFile = {\n name,\n hash,\n ext: file.ext,\n mime: file.mime,\n filepath: filePath,\n path: file.path || null,\n getStream: () => fs.createReadStream(filePath),\n };\n\n Object.assign(newFile, {\n width,\n height,\n size: size ? bytesToKbytes(size) : 0,\n sizeInBytes: size,\n });\n return newFile;\n};\n\nconst generateThumbnail = async (file: UploadableFile) => {\n if (\n file.width &&\n file.height &&\n (file.width > THUMBNAIL_RESIZE_OPTIONS.width || file.height > THUMBNAIL_RESIZE_OPTIONS.height)\n ) {\n return resizeFileTo(file, THUMBNAIL_RESIZE_OPTIONS, {\n name: `thumbnail_${file.name}`,\n hash: `thumbnail_${file.hash}`,\n });\n }\n\n return null;\n};\n\n/**\n * Optimize image by:\n * - auto orienting image based on EXIF data\n * - reduce image quality\n *\n */\nconst optimize = async (file: UploadableFile) => {\n const { sizeOptimization = false, autoOrientation = false } =\n (await getService('upload').getSettings()) ?? {};\n\n const { format, size } = await getMetadata(file);\n\n if ((sizeOptimization || autoOrientation) && isOptimizableFormat(format)) {\n let transformer;\n if (!file.filepath) {\n transformer = sharp();\n } else {\n transformer = sharp(file.filepath);\n }\n // reduce image quality\n transformer[format]({ quality: sizeOptimization ? 80 : 100 });\n // rotate image based on EXIF data\n if (autoOrientation) {\n transformer.rotate();\n }\n const filePath = file.tmpWorkingDirectory\n ? join(file.tmpWorkingDirectory, `optimized-${file.hash}`)\n : `optimized-${file.hash}`;\n\n let newInfo;\n if (!file.filepath) {\n transformer.on('info', (info) => {\n newInfo = info;\n });\n\n await writeStreamToFile(file.getStream().pipe(transformer), filePath);\n } else {\n newInfo = await transformer.toFile(filePath);\n }\n\n const { width: newWidth, height: newHeight, size: newSize } = newInfo ?? {};\n\n const newFile = { ...file };\n\n newFile.getStream = () => fs.createReadStream(filePath);\n newFile.filepath = filePath;\n\n if (newSize && size && newSize > size) {\n // Ignore optimization if output is bigger than original\n return file;\n }\n\n return Object.assign(newFile, {\n width: newWidth,\n height: newHeight,\n size: newSize ? bytesToKbytes(newSize) : 0,\n sizeInBytes: newSize,\n });\n }\n\n return file;\n};\n\nconst DEFAULT_BREAKPOINTS = {\n large: 1000,\n medium: 750,\n small: 500,\n};\n\nconst getBreakpoints = () =>\n strapi.config.get<Record<string, number>>('plugin::upload.breakpoints', DEFAULT_BREAKPOINTS);\n\nconst generateResponsiveFormats = async (file: UploadableFile) => {\n const { responsiveDimensions = false } = (await getService('upload').getSettings()) ?? {};\n\n if (!responsiveDimensions) return [];\n\n const originalDimensions = await getDimensions(file);\n\n const breakpoints = getBreakpoints();\n const results = [];\n\n for (const key of Object.keys(breakpoints)) {\n const breakpoint = breakpoints[key];\n\n if (breakpointSmallerThan(breakpoint, originalDimensions)) {\n results.push(await generateBreakpoint(key, { file, breakpoint }));\n }\n }\n\n return results;\n};\n\nconst generateBreakpoint = async (\n key: string,\n { file, breakpoint }: { file: UploadableFile; breakpoint: number }\n) => {\n const newFile = await resizeFileTo(\n file,\n {\n width: breakpoint,\n height: breakpoint,\n fit: 'inside',\n },\n {\n name: `${key}_${file.name}`,\n hash: `${key}_${file.hash}`,\n }\n );\n return {\n key,\n file: newFile,\n };\n};\n\nconst breakpointSmallerThan = (breakpoint: number, { width, height }: Dimensions) => {\n return breakpoint < (width ?? 0) || breakpoint < (height ?? 0);\n};\n\n/**\n * Applies a simple image transformation to see if the image is faulty/corrupted.\n */\nconst isFaultyImage = async (file: UploadableFile) => {\n if (!file.filepath) {\n return new Promise((resolve, reject) => {\n const pipeline = sharp();\n pipeline.stats().then(resolve).catch(reject);\n file.getStream().pipe(pipeline);\n });\n }\n\n try {\n await sharp(file.filepath).stats();\n return false;\n } catch (e) {\n return true;\n }\n};\n\nconst isOptimizableImage = async (file: UploadableFile) => {\n let format;\n try {\n const metadata = await getMetadata(file);\n format = metadata.format;\n } catch (e) {\n // throw when the file is not a supported image\n return false;\n }\n return format && FORMATS_TO_OPTIMIZE.includes(format);\n};\n\nconst isResizableImage = async (file: UploadableFile) => {\n let format;\n try {\n const metadata = await getMetadata(file);\n format = metadata.format;\n } catch (e) {\n // throw when the file is not a supported image\n return false;\n }\n return format && FORMATS_TO_RESIZE.includes(format);\n};\n\nconst isImage = async (file: UploadableFile) => {\n let format;\n try {\n const metadata = await getMetadata(file);\n format = metadata.format;\n } catch (e) {\n // throw when the file is not a supported image\n return false;\n }\n return format && FORMATS_TO_PROCESS.includes(format);\n};\n\nconst generateFileName = (name: string) => {\n const randomSuffix = () => crypto.randomBytes(5).toString('hex');\n const baseName = strings.nameToSlug(name, { separator: '_', lowercase: false });\n\n return `${baseName}_${randomSuffix()}`;\n};\n\nexport default {\n isFaultyImage,\n isOptimizableImage,\n isResizableImage,\n isImage,\n getDimensions,\n generateResponsiveFormats,\n generateThumbnail,\n optimize,\n generateFileName,\n};\n"],"names":["bytesToKbytes","fileUtils","FORMATS_TO_RESIZE","FORMATS_TO_PROCESS","FORMATS_TO_OPTIMIZE","isOptimizableFormat","format","undefined","includes","writeStreamToFile","stream","path","Promise","resolve","reject","writeStream","fs","createWriteStream","on","pipe","getMetadata","file","filepath","pipeline","sharp","metadata","then","catch","getStream","getDimensions","width","height","THUMBNAIL_RESIZE_OPTIONS","fit","resizeFileTo","options","name","hash","filePath","tmpWorkingDirectory","join","newInfo","transform","resize","info","toFile","size","newFile","ext","mime","createReadStream","Object","assign","sizeInBytes","generateThumbnail","optimize","sizeOptimization","autoOrientation","getService","getSettings","transformer","quality","rotate","newWidth","newHeight","newSize","DEFAULT_BREAKPOINTS","large","medium","small","getBreakpoints","strapi","config","get","generateResponsiveFormats","responsiveDimensions","originalDimensions","breakpoints","results","key","keys","breakpoint","breakpointSmallerThan","push","generateBreakpoint","isFaultyImage","stats","e","isOptimizableImage","isResizableImage","isImage","generateFileName","randomSuffix","crypto","randomBytes","toString","baseName","strings","nameToSlug","separator","lowercase"],"mappings":"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"}
1
+ {"version":3,"file":"image-manipulation.mjs","sources":["../../../server/src/services/image-manipulation.ts"],"sourcesContent":["import fs from 'fs';\nimport { join } from 'path';\nimport sharp from 'sharp';\nimport crypto from 'crypto';\nimport { strings, file as fileUtils } from '@strapi/utils';\n\nimport { getService } from '../utils';\n\nimport type { UploadableFile } from '../types';\n\ntype Dimensions = {\n width: number | null;\n height: number | null;\n};\n\n// TODO: remove after upgrading sharp to >=0.34.2 (pageHeight added to OutputInfo types)\ndeclare module 'sharp' {\n interface OutputInfo {\n pageHeight?: number;\n }\n}\n\nconst { bytesToKbytes } = fileUtils;\n\nconst FORMATS_TO_RESIZE = ['jpeg', 'png', 'webp', 'tiff', 'gif'];\nconst FORMATS_TO_PROCESS = ['jpeg', 'png', 'webp', 'tiff', 'svg', 'gif', 'avif'];\nconst FORMATS_TO_OPTIMIZE = ['jpeg', 'png', 'webp', 'tiff', 'avif'];\n\nconst isOptimizableFormat = (\n format: string | undefined\n): format is 'jpeg' | 'png' | 'webp' | 'tiff' | 'avif' =>\n format !== undefined && FORMATS_TO_OPTIMIZE.includes(format);\n\nconst writeStreamToFile = (stream: NodeJS.ReadWriteStream, path: string) =>\n new Promise<void>((resolve, reject) => {\n const writeStream = fs.createWriteStream(path);\n // Reject promise if there is an error with the provided stream\n stream.on('error', reject);\n stream.pipe(writeStream);\n writeStream.on('close', () => resolve());\n writeStream.on('error', reject);\n });\n\nconst getMetadata = (file: UploadableFile): Promise<sharp.Metadata> => {\n if (!file.filepath) {\n return new Promise((resolve, reject) => {\n const pipeline = sharp();\n pipeline.metadata().then(resolve).catch(reject);\n file.getStream().pipe(pipeline);\n });\n }\n\n return sharp(file.filepath).metadata();\n};\n\nconst getDimensions = async (file: UploadableFile): Promise<Dimensions> => {\n const { width = null, height = null } = await getMetadata(file);\n\n return { width, height };\n};\n\nconst THUMBNAIL_RESIZE_OPTIONS = {\n width: 245,\n height: 156,\n fit: 'inside',\n} satisfies sharp.ResizeOptions;\n\nconst resizeFileTo = async (\n file: UploadableFile,\n options: sharp.ResizeOptions,\n {\n name,\n hash,\n }: {\n name: string;\n hash: string;\n }\n) => {\n const filePath = file.tmpWorkingDirectory ? join(file.tmpWorkingDirectory, hash) : hash;\n\n let newInfo;\n if (!file.filepath) {\n const transform = sharp({ animated: true })\n .resize(options)\n .on('info', (info) => {\n newInfo = info;\n });\n\n await writeStreamToFile(file.getStream().pipe(transform), filePath);\n } else {\n newInfo = await sharp(file.filepath, { animated: true }).resize(options).toFile(filePath);\n }\n\n const { width, height, size, pageHeight } = newInfo ?? {};\n\n const newFile: UploadableFile = {\n name,\n hash,\n ext: file.ext,\n mime: file.mime,\n filepath: filePath,\n path: file.path || null,\n getStream: () => fs.createReadStream(filePath),\n };\n\n Object.assign(newFile, {\n width,\n height: pageHeight ?? height,\n size: size ? bytesToKbytes(size) : 0,\n sizeInBytes: size,\n });\n return newFile;\n};\n\nconst generateThumbnail = async (file: UploadableFile) => {\n if (\n file.width &&\n file.height &&\n (file.width > THUMBNAIL_RESIZE_OPTIONS.width || file.height > THUMBNAIL_RESIZE_OPTIONS.height)\n ) {\n return resizeFileTo(file, THUMBNAIL_RESIZE_OPTIONS, {\n name: `thumbnail_${file.name}`,\n hash: `thumbnail_${file.hash}`,\n });\n }\n\n return null;\n};\n\n/**\n * Optimize image by:\n * - auto orienting image based on EXIF data\n * - reduce image quality\n *\n */\nconst optimize = async (file: UploadableFile) => {\n const { sizeOptimization = false, autoOrientation = false } =\n (await getService('upload').getSettings()) ?? {};\n\n const { format, size } = await getMetadata(file);\n\n if ((sizeOptimization || autoOrientation) && isOptimizableFormat(format)) {\n let transformer;\n if (!file.filepath) {\n transformer = sharp({ animated: true });\n } else {\n transformer = sharp(file.filepath, { animated: true });\n }\n // reduce image quality\n transformer[format]({ quality: sizeOptimization ? 80 : 100 });\n // rotate image based on EXIF data\n if (autoOrientation) {\n transformer.rotate();\n }\n const filePath = file.tmpWorkingDirectory\n ? join(file.tmpWorkingDirectory, `optimized-${file.hash}`)\n : `optimized-${file.hash}`;\n\n let newInfo;\n if (!file.filepath) {\n transformer.on('info', (info) => {\n newInfo = info;\n });\n\n await writeStreamToFile(file.getStream().pipe(transformer), filePath);\n } else {\n newInfo = await transformer.toFile(filePath);\n }\n\n const {\n width: newWidth,\n height: newHeight,\n size: newSize,\n pageHeight: newPageHeight,\n } = newInfo ?? {};\n\n const newFile = { ...file };\n\n newFile.getStream = () => fs.createReadStream(filePath);\n newFile.filepath = filePath;\n\n if (newSize && size && newSize > size) {\n // Ignore optimization if output is bigger than original\n return file;\n }\n\n return Object.assign(newFile, {\n width: newWidth,\n height: newPageHeight ?? newHeight,\n size: newSize ? bytesToKbytes(newSize) : 0,\n sizeInBytes: newSize,\n });\n }\n\n return file;\n};\n\nconst DEFAULT_BREAKPOINTS = {\n large: 1000,\n medium: 750,\n small: 500,\n};\n\nconst getBreakpoints = () =>\n strapi.config.get<Record<string, number>>('plugin::upload.breakpoints', DEFAULT_BREAKPOINTS);\n\nconst generateResponsiveFormats = async (file: UploadableFile) => {\n const { responsiveDimensions = false } = (await getService('upload').getSettings()) ?? {};\n\n if (!responsiveDimensions) return [];\n\n const originalDimensions = await getDimensions(file);\n\n const breakpoints = getBreakpoints();\n const results = [];\n\n for (const key of Object.keys(breakpoints)) {\n const breakpoint = breakpoints[key];\n\n if (breakpointSmallerThan(breakpoint, originalDimensions)) {\n results.push(await generateBreakpoint(key, { file, breakpoint }));\n }\n }\n\n return results;\n};\n\nconst generateBreakpoint = async (\n key: string,\n { file, breakpoint }: { file: UploadableFile; breakpoint: number }\n) => {\n const newFile = await resizeFileTo(\n file,\n {\n width: breakpoint,\n height: breakpoint,\n fit: 'inside',\n },\n {\n name: `${key}_${file.name}`,\n hash: `${key}_${file.hash}`,\n }\n );\n return {\n key,\n file: newFile,\n };\n};\n\nconst breakpointSmallerThan = (breakpoint: number, { width, height }: Dimensions) => {\n return breakpoint < (width ?? 0) || breakpoint < (height ?? 0);\n};\n\n/**\n * Applies a simple image transformation to see if the image is faulty/corrupted.\n */\nconst isFaultyImage = async (file: UploadableFile) => {\n if (!file.filepath) {\n return new Promise((resolve, reject) => {\n const pipeline = sharp();\n pipeline.stats().then(resolve).catch(reject);\n file.getStream().pipe(pipeline);\n });\n }\n\n try {\n await sharp(file.filepath).stats();\n return false;\n } catch (e) {\n return true;\n }\n};\n\nconst isOptimizableImage = async (file: UploadableFile) => {\n let format;\n try {\n const metadata = await getMetadata(file);\n format = metadata.format;\n } catch (e) {\n // throw when the file is not a supported image\n return false;\n }\n return format && FORMATS_TO_OPTIMIZE.includes(format);\n};\n\nconst isResizableImage = async (file: UploadableFile) => {\n let format;\n try {\n const metadata = await getMetadata(file);\n format = metadata.format;\n } catch (e) {\n // throw when the file is not a supported image\n return false;\n }\n return format && FORMATS_TO_RESIZE.includes(format);\n};\n\nconst isImage = async (file: UploadableFile) => {\n let format;\n try {\n const metadata = await getMetadata(file);\n format = metadata.format;\n } catch (e) {\n // throw when the file is not a supported image\n return false;\n }\n return format && FORMATS_TO_PROCESS.includes(format);\n};\n\nconst generateFileName = (name: string) => {\n const randomSuffix = () => crypto.randomBytes(5).toString('hex');\n const baseName = strings.nameToSlug(name, { separator: '_', lowercase: false });\n\n return `${baseName}_${randomSuffix()}`;\n};\n\nexport default {\n isFaultyImage,\n isOptimizableImage,\n isResizableImage,\n isImage,\n getDimensions,\n generateResponsiveFormats,\n generateThumbnail,\n optimize,\n generateFileName,\n};\n"],"names":["bytesToKbytes","fileUtils","FORMATS_TO_RESIZE","FORMATS_TO_PROCESS","FORMATS_TO_OPTIMIZE","isOptimizableFormat","format","undefined","includes","writeStreamToFile","stream","path","Promise","resolve","reject","writeStream","fs","createWriteStream","on","pipe","getMetadata","file","filepath","pipeline","sharp","metadata","then","catch","getStream","getDimensions","width","height","THUMBNAIL_RESIZE_OPTIONS","fit","resizeFileTo","options","name","hash","filePath","tmpWorkingDirectory","join","newInfo","transform","animated","resize","info","toFile","size","pageHeight","newFile","ext","mime","createReadStream","Object","assign","sizeInBytes","generateThumbnail","optimize","sizeOptimization","autoOrientation","getService","getSettings","transformer","quality","rotate","newWidth","newHeight","newSize","newPageHeight","DEFAULT_BREAKPOINTS","large","medium","small","getBreakpoints","strapi","config","get","generateResponsiveFormats","responsiveDimensions","originalDimensions","breakpoints","results","key","keys","breakpoint","breakpointSmallerThan","push","generateBreakpoint","isFaultyImage","stats","e","isOptimizableImage","isResizableImage","isImage","generateFileName","randomSuffix","crypto","randomBytes","toString","baseName","strings","nameToSlug","separator","lowercase"],"mappings":"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"}
@@ -7,6 +7,7 @@ var fse = require('fs-extra');
7
7
  var _ = require('lodash');
8
8
  var mimeTypes = require('mime-types');
9
9
  var utils = require('@strapi/utils');
10
+ var fp = require('lodash/fp');
10
11
  var constants = require('../constants.js');
11
12
  var index = require('../utils/index.js');
12
13
 
@@ -261,7 +262,7 @@ var upload = (({ strapi })=>{
261
262
  caption: _.isNil(caption) ? dbFile.caption : caption,
262
263
  focalPoint: _.isNil(focalPoint) ? dbFile.focalPoint : focalPoint,
263
264
  folder: _.isUndefined(folder) ? dbFile.folder : folder,
264
- folderPath: _.isUndefined(folder) ? dbFile.path : await fileService.getFolderPath(folder)
265
+ folderPath: _.isUndefined(folder) ? dbFile.folderPath : await fileService.getFolderPath(folder)
265
266
  };
266
267
  return update(id, newInfos, {
267
268
  user
@@ -377,6 +378,93 @@ var upload = (({ strapi })=>{
377
378
  results: signedResults
378
379
  };
379
380
  }
381
+ /**
382
+ * Resolve whether the count query should run, mirroring core-api's `shouldCount`.
383
+ * Defaults to the `api.rest.withCount` config (true) when not specified on the request.
384
+ */ function resolveWithCount(pagination) {
385
+ if (fp.has('withCount', pagination)) {
386
+ const withCount = pagination.withCount;
387
+ if (typeof withCount === 'boolean') {
388
+ return withCount;
389
+ }
390
+ if (typeof withCount === 'undefined') {
391
+ return false;
392
+ }
393
+ if ([
394
+ 'true',
395
+ 't',
396
+ '1',
397
+ 1
398
+ ].includes(withCount)) {
399
+ return true;
400
+ }
401
+ if ([
402
+ 'false',
403
+ 'f',
404
+ '0',
405
+ 0
406
+ ].includes(withCount)) {
407
+ return false;
408
+ }
409
+ throw new utils.errors.ValidationError('Invalid withCount parameter. Expected "t","1","true","false","0","f"');
410
+ }
411
+ return Boolean(strapi.config.get('api.rest.withCount', true));
412
+ }
413
+ /**
414
+ * REST-aware paginated find for the content API.
415
+ *
416
+ * Unlike `findPage` (used by the admin API), this honors the standard nested
417
+ * `pagination` query object (`pagination[page]`, `pagination[pageSize]`,
418
+ * `pagination[start]`, `pagination[limit]`, `pagination[withCount]`) and the
419
+ * `api.rest.defaultLimit` / `api.rest.maxLimit` / `api.rest.withCount` config,
420
+ * exactly like every other Strapi REST collection-type endpoint.
421
+ */ async function findAndCountPage(query = {}) {
422
+ const { pagination = {} } = query;
423
+ const defaultLimit = fp.toNumber(strapi.config.get('api.rest.defaultLimit', 25));
424
+ const maxLimit = fp.toNumber(strapi.config.get('api.rest.maxLimit')) || null;
425
+ // Whether the consumer used page-based pagination (default) vs offset-based.
426
+ const isOffset = fp.has('start', pagination) || fp.has('limit', pagination);
427
+ const isPaged = !isOffset;
428
+ // Resolve start/limit applying defaults and the maxLimit cap.
429
+ const { start, limit } = utils.pagination.withDefaultPagination(pagination, {
430
+ defaults: {
431
+ offset: {
432
+ limit: defaultLimit
433
+ },
434
+ page: {
435
+ pageSize: defaultLimit
436
+ }
437
+ },
438
+ maxLimit: maxLimit || -1
439
+ });
440
+ // Feed the transform the resolved offset/limit so it ignores the nested
441
+ // `pagination` object (which it cannot read) and applies real bounds.
442
+ const transformed = strapi.get('query-params').transform(constants.FILE_MODEL_UID, {
443
+ ...query,
444
+ pagination: undefined,
445
+ start,
446
+ limit
447
+ });
448
+ const shouldCount = resolveWithCount(pagination);
449
+ const [results, total] = await Promise.all([
450
+ strapi.db.query(constants.FILE_MODEL_UID).findMany(transformed),
451
+ shouldCount ? strapi.db.query(constants.FILE_MODEL_UID).count(transformed) : Promise.resolve(undefined)
452
+ ]);
453
+ const signedResults = await utils.async.map(results, (file)=>fileService.signFileUrls(file));
454
+ const transform = isPaged ? utils.pagination.transformPagedPaginationInfo : utils.pagination.transformOffsetPaginationInfo;
455
+ const paginationInfo = transform({
456
+ start,
457
+ limit
458
+ }, total);
459
+ return {
460
+ results: signedResults,
461
+ // Omit total & pageCount when counting is disabled (withCount=false).
462
+ pagination: fp.isNil(total) ? _.omit(paginationInfo, [
463
+ 'total',
464
+ 'pageCount'
465
+ ]) : paginationInfo
466
+ };
467
+ }
380
468
  async function remove(file) {
381
469
  const config = strapi.config.get('plugin::upload');
382
470
  // execute delete function of the provider
@@ -448,6 +536,7 @@ var upload = (({ strapi })=>{
448
536
  findOne,
449
537
  findMany,
450
538
  findPage,
539
+ findAndCountPage,
451
540
  remove,
452
541
  getSettings,
453
542
  setSettings,