Merge pull request #787 from directus/assets

Assets fixes and improvements.
This commit is contained in:
Rijk van Zanten
2020-10-29 21:05:45 +01:00
committed by GitHub
10 changed files with 162 additions and 119 deletions

View File

@@ -3,37 +3,37 @@ import { Transformation } from './types/assets';
export const SYSTEM_ASSET_ALLOW_LIST: Transformation[] = [
{
key: 'system-small-cover',
w: 64,
h: 64,
f: 'cover',
width: 64,
height: 64,
fit: 'cover',
},
{
key: 'system-small-contain',
w: 64,
f: 'contain',
width: 64,
fit: 'contain',
},
{
key: 'system-medium-cover',
w: 300,
h: 300,
f: 'cover',
width: 300,
height: 300,
fit: 'cover',
},
{
key: 'system-medium-contain',
w: 300,
f: 'contain',
width: 300,
fit: 'contain',
},
{
key: 'system-large-cover',
w: 800,
h: 600,
f: 'cover',
width: 800,
height: 600,
fit: 'cover',
},
{
key: 'system-large-contain',
w: 800,
f: 'contain',
width: 800,
fit: 'contain',
},
];
export const ASSET_TRANSFORM_QUERY_KEYS = ['key', 'w', 'h', 'f'];
export const ASSET_TRANSFORM_QUERY_KEYS = ['key', 'width', 'height', 'fit', 'withoutEnlargement'];

View File

@@ -9,7 +9,6 @@ import { Transformation } from '../types/assets';
import storage from '../storage';
import { PayloadService, AssetsService } from '../services';
import useCollection from '../middleware/use-collection';
import { respond } from '../middleware/respond';
const router = Router();
@@ -79,18 +78,20 @@ router.get(
];
// For use in the next request handler
res.locals.shortcuts = [...SYSTEM_ASSET_ALLOW_LIST, assetSettings.storage_asset_presets];
res.locals.shortcuts = [
...SYSTEM_ASSET_ALLOW_LIST,
...(assetSettings.storage_asset_presets || []),
];
res.locals.transformation = transformation;
if (Object.keys(transformation).length === 0) {
return next();
}
if (assetSettings.asset_generation === 'all') {
if (assetSettings.storage_asset_transform === 'all') {
if (transformation.key && allKeys.includes(transformation.key as string) === false)
throw new InvalidQueryException(`Key "${transformation.key}" isn't configured.`);
return next();
} else if (assetSettings.asset_generation === 'shortcut') {
} else if (assetSettings.storage_asset_transform === 'shortcut') {
if (allKeys.includes(transformation.key as string)) return next();
throw new InvalidQueryException(
`Only configured shortcuts can be used in asset generation.`

View File

@@ -132,6 +132,11 @@ fields:
text: Contain (preserve aspect ratio)
- value: cover
text: Cover (forces exact size)
- value: inside
text: Fit inside
- value: outside
text: Fit outside
required: true
width: half
- field: width
name: Width
@@ -161,7 +166,17 @@ fields:
max: 100
min: 0
step: 1
width: full
required: true
width: half
- field: withoutEnlargement
type: boolean
schema:
default_value: false
meta:
interface: toggle
width: half
options:
label: Don't upscale images
template: '{{key}}'
special: json
width: full

View File

@@ -48,9 +48,11 @@ export class AssetsService {
private parseTransformation(transformation: Transformation): ResizeOptions {
const resizeOptions: ResizeOptions = {};
if (transformation.w) resizeOptions.width = Number(transformation.w);
if (transformation.h) resizeOptions.height = Number(transformation.h);
if (transformation.f) resizeOptions.fit = transformation.f;
if (transformation.width) resizeOptions.width = Number(transformation.width);
if (transformation.height) resizeOptions.height = Number(transformation.height);
if (transformation.fit) resizeOptions.fit = transformation.fit;
if (transformation.withoutEnlargement)
resizeOptions.withoutEnlargement = Boolean(transformation.withoutEnlargement);
return resizeOptions;
}

View File

@@ -1,8 +1,9 @@
export type Transformation = {
key?: string;
w?: number; // width
h?: number; // height
f?: 'cover' | 'contain'; // fit
width?: number; // width
height?: number; // height
fit?: 'cover' | 'contain' | 'inside' | 'outside'; // fit
withoutEnlargement?: boolean; // Without Enlargement
};
// @NOTE Keys used in Transformation should match ASSET_GENERATION_QUERY_KEYS in constants.ts

View File

@@ -11364,7 +11364,9 @@
"options": {
"choices": {
"contain": "Contain (preserve aspect ratio)",
"crop": "Crop (forces exact size)"
"crop": "Crop (forces exact size)",
"inside": "Fit inside",
"outside": "Fit outside"
}
},
"required": true,
@@ -11387,6 +11389,18 @@
"type": "integer",
"width": "half"
},
{
"default_value": false,
"field": "withoutEnlargement",
"interface": "toggle",
"name": "Don't upscale images",
"required": false,
"type": "boolean",
"width": "half",
"options": {
"label": "Don't upscale images"
}
},
{
"default_value": 80,
"field": "quality",
@@ -11399,7 +11413,7 @@
},
"required": true,
"type": "integer",
"width": "full"
"width": "half"
}
],
"template": "{{key}}"

162
app/package-lock.json generated
View File

@@ -6601,6 +6601,51 @@
"tslint": "^5.20.1",
"webpack": "^4.0.0",
"yorkie": "^2.0.0"
},
"dependencies": {
"chalk": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
"dev": true,
"optional": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"fork-ts-checker-webpack-plugin-v5": {
"version": "npm:fork-ts-checker-webpack-plugin@5.2.0",
"resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-5.2.0.tgz",
"integrity": "sha512-NEKcI0+osT5bBFZ1SFGzJMQETjQWZrSvMO1g0nAR/w0t328Z41eN8BJEIZyFCl2HsuiJpa9AN474Nh2qLVwGLQ==",
"dev": true,
"optional": true,
"requires": {
"@babel/code-frame": "^7.8.3",
"@types/json-schema": "^7.0.5",
"chalk": "^4.1.0",
"cosmiconfig": "^6.0.0",
"deepmerge": "^4.2.2",
"fs-extra": "^9.0.0",
"memfs": "^3.1.2",
"minimatch": "^3.0.4",
"schema-utils": "2.7.0",
"semver": "^7.3.2",
"tapable": "^1.0.0"
}
},
"schema-utils": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz",
"integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==",
"dev": true,
"optional": true,
"requires": {
"@types/json-schema": "^7.0.4",
"ajv": "^6.12.2",
"ajv-keywords": "^3.4.1"
}
}
}
},
"@vue/cli-plugin-unit-jest": {
@@ -6740,6 +6785,17 @@
"unique-filename": "^1.1.1"
}
},
"chalk": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
"dev": true,
"optional": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"cliui": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
@@ -6823,6 +6879,18 @@
"graceful-fs": "^4.1.6"
}
},
"loader-utils": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz",
"integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==",
"dev": true,
"optional": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^2.1.2"
}
},
"locate-path": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
@@ -6936,6 +7004,18 @@
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
"dev": true
},
"vue-loader-v16": {
"version": "npm:vue-loader@16.0.0-beta.8",
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.0.0-beta.8.tgz",
"integrity": "sha512-oouKUQWWHbSihqSD7mhymGPX1OQ4hedzAHyvm8RdyHh6m3oIvoRF+NM45i/bhNOlo8jCnuJhaSUf/6oDjv978g==",
"dev": true,
"optional": true,
"requires": {
"chalk": "^4.1.0",
"hash-sum": "^2.0.0",
"loader-utils": "^2.0.0"
}
},
"wrap-ansi": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
@@ -11664,51 +11744,6 @@
}
}
},
"fork-ts-checker-webpack-plugin-v5": {
"version": "npm:fork-ts-checker-webpack-plugin@5.2.0",
"resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-5.2.0.tgz",
"integrity": "sha512-NEKcI0+osT5bBFZ1SFGzJMQETjQWZrSvMO1g0nAR/w0t328Z41eN8BJEIZyFCl2HsuiJpa9AN474Nh2qLVwGLQ==",
"dev": true,
"optional": true,
"requires": {
"@babel/code-frame": "^7.8.3",
"@types/json-schema": "^7.0.5",
"chalk": "^4.1.0",
"cosmiconfig": "^6.0.0",
"deepmerge": "^4.2.2",
"fs-extra": "^9.0.0",
"memfs": "^3.1.2",
"minimatch": "^3.0.4",
"schema-utils": "2.7.0",
"semver": "^7.3.2",
"tapable": "^1.0.0"
},
"dependencies": {
"chalk": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
"dev": true,
"optional": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"schema-utils": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz",
"integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==",
"dev": true,
"optional": true,
"requires": {
"@types/json-schema": "^7.0.4",
"ajv": "^6.12.2",
"ajv-keywords": "^3.4.1"
}
}
}
},
"form-data": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
@@ -20342,43 +20377,6 @@
}
}
},
"vue-loader-v16": {
"version": "npm:vue-loader@16.0.0-beta.8",
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.0.0-beta.8.tgz",
"integrity": "sha512-oouKUQWWHbSihqSD7mhymGPX1OQ4hedzAHyvm8RdyHh6m3oIvoRF+NM45i/bhNOlo8jCnuJhaSUf/6oDjv978g==",
"dev": true,
"optional": true,
"requires": {
"chalk": "^4.1.0",
"hash-sum": "^2.0.0",
"loader-utils": "^2.0.0"
},
"dependencies": {
"chalk": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
"dev": true,
"optional": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"loader-utils": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz",
"integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==",
"dev": true,
"optional": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^2.1.2"
}
}
}
},
"vue-router": {
"version": "3.4.6",
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.4.6.tgz",

View File

@@ -40,14 +40,16 @@ The **Storage Asset Transform** can be used in conjunction with the presets to f
Fetching thumbnails is as easy as adding query parameters to the original file's URL. If a requested thumbnail doesn't yet exist, it is dynamically generated and immediately returned. When requesting a thumbnail, the following parameters are all required.
* **`f`** — The **fit** of the thumbnail, either `crop` or `contain`
* **`w`** — The **width** of the thumbnail in pixels
* **`h`** — The **height** of the thumbnail in pixels
* **`q`** — The **quality** of the thumbnail (`0` to `100`)
* **`fit`** — The **fit** of the thumbnail, either `crop` or `contain`
* **`width`** — The **width** of the thumbnail in pixels
* **`height`** — The **height** of the thumbnail in pixels
* **`quality`** — The **quality** of the thumbnail (`0` to `100`)
* **`withoutEnlargement`** — Disable image up-scaling
* **`download`** — Add `Content-Disposition` header and force browser to download file
```
example.com/assets/<file-id>?f=<fit>&w=<width>&h=<height>&q=<quality>
example.com/assets/1ac73658-8b62-4dea-b6da-529fbc9d01a4?f=crop&w=200&h=200&q=80
example.com/assets/<file-id>?fit=<fit>&width=<width>&height=<height>&quality=<quality>
example.com/assets/1ac73658-8b62-4dea-b6da-529fbc9d01a4?fit=crop&width=200&height=200&quality=80
```
Alternatively, you can reference a specific thumbnail by its preset key.

View File

@@ -71,12 +71,17 @@ properties:
enum:
- cover
- contain
- inside
- outside
width:
description: Width of the thumbnail.
type: integer
height:
description: Height of the thumbnail.
type: integer
withoutEnlargement:
description: No image upscale
type: boolean
quality:
description: Quality of the compression used.
type: integer

View File

@@ -32,7 +32,12 @@ get:
description: Fit of the file
schema:
type: string
enum: [crop, contain]
enum: [crop, contain, inside, outside]
- name: withoutEnlargement
in: query
description: No image upscale.
schema:
type: boolean
- name: quality
in: query
description: Quality of compression.