My goal with this post is to describe how Strapi’s upload plugin can be customized to enable uploading assets to different destinations based on what the Admin UI user selects during the file upload.
Leveraging these changes, we can customize the Strapi upload plugin to do things like -
Modifying the schema.json
for the plugin datatype models or extending the server-side API can be achieved by Strapi plugins extension. But, this does not cover customizing the Admin panel UI part of the plugin.
As a result, customizing the admin UI part for any Strapi plugin requires using patch-package. A limitation of using patch-package
is that the changes need to be carefully evaluated everytime the customized plugin version is updated.
yarn create strapi-app strapi-customize-upload
to have a fresh running strapi instance.patch-package
to perform our customizations, let’s install it via yarn add patch-package
mkdir providers
mkdir providers/strapi-provider-upload-custom
providers/strapi-provider-upload-custom
folder).providers/strapi-provider-upload-custom/package.json
"name": "strapi-provider-upload-custom",
"version": "0.0.1",
"description": "Custom provider for strapi-upload plugin",
const S3LocationDefault = new AWS.S3({
apiVersion: '2006-03-01',
...config.s3LocationDefault,
});
const S3Location2 = new AWS.S3({
apiVersion: '2006-03-01',
...config.s3Location2,
});
file.uploadDestination
) that will let our provider code decide where to upload a certain file:let S3 = file.uploadDestination &&
file.uploadDestination === 's3Location2' ?
S3Location2 : S3LocationDefault;
file.provider_metadata = {
uploadDestination: file.uploadDestination ||
's3LocationDefault'};
config.s3LocationDefault
and the config.s3Location2
used within our custom provider code in the previous section need to be defined within our plugin config (config/plugins.js
):module.exports = ({ env }) => ({
// ...
upload: {
config: {
provider: 'strapi-provider-upload-custom',
providerOptions: {
s3LocationDefault : {
accessKeyId: env('AWS_ACCESS_KEY_ID'),
secretAccessKey: env('AWS_ACCESS_SECRET'),
region: env('AWS_REGION'),
params: {
Bucket: env('AWS_BUCKET'),
},
},
s3Location2 : {
accessKeyId: env('AWS_ACCESS_KEY_ID_2'),
secretAccessKey: env('AWS_ACCESS_SECRET_2'),
region: env('AWS_REGION_2'),
params: {
Bucket: env('AWS_BUCKET_2'),
},
},
},
},
},
// ...
});
.env
file.config/middlewares.js
:{
name: 'strapi::security',
config: {
contentSecurityPolicy: {
useDefaults: true,
directives: {
'connect-src': ["'self'", 'https:'],
'img-src': ["'self'", 'data:', 'blob:', process.env.AWS_BUCKET + ".s3." + process.env.AWS_REGION + ".amazonaws.com", process.env.AWS_BUCKET + ".s3.amazonaws.com", process.env.AWS_BUCKET_2 + ".s3." + process.env.AWS_REGION_2 + ".amazonaws.com", process.env.AWS_BUCKET_2 + ".s3.amazonaws.com"],
'media-src': ["'self'", 'data:', 'blob:', process.env.AWS_BUCKET + ".s3." + process.env.AWS_REGION + ".amazonaws.com", process.env.AWS_BUCKET + ".s3.amazonaws.com", process.env.AWS_BUCKET_2 + ".s3." + process.env.AWS_REGION_2 + ".amazonaws.com",process.env.AWS_BUCKET_2 + ".s3.amazonaws.com"],
upgradeInsecureRequests: null,
}
}
}
},
providers/strapi-provider-upload-custom
folder via yarn install
strapi-provider-upload-custom
to our Strapi setup by adding the following line within the main package.json
(see this file for reference):"strapi-provider-upload-custom": "file:providers/strapi-provider-upload-custom"
yarn
yarn build
yarn develop
strapi-provider-upload-custom
is working as expected by trying to upload a few files via the Strapi Admin UI. At this point, all the uploaded files will go to the default S3 bucket (specified within .env
AWS_BUCKET
variable).patch-package
for this part of the customization, we need to modify the node_modules/@strapi/plugin-upload
code on our local setup. (I prefer opening this folder in a separate instance of my code editor).admin/src/components/UploadAssetDialog/PendingAssetStep/PendingAssetStep.js
file.const UploadDestinationSelect = ({value, setValue}) => {
return (
<Combobox label="Upload Destination" value={value} onChange={setValue}>
<ComboboxOption value="s3LocationDefault">Default Upload Destination</ComboboxOption>
<ComboboxOption value="s3Location2">Upload Destination 2</ComboboxOption>
</Combobox>
);
}
const [selectedDestination, setSelectedDestination] = useState('s3LocationDefault');
uploadDestination
:asset.uploadDestination = selectedDestination
plugin-upload/admin/src/hooks/useUpload.js
to pass this asset.uploadDestination
value via the HTTP POST
request it makes to upload the selected file. const { rawFile, caption, name, alternativeText, uploadDestination } = asset;
...
...
formData.append(
'fileInfo',
JSON.stringify({
name,
caption,
alternativeText,
folder: folderId,
uploadDestination
})
);
uploadDestination
(like the rest of the properties).uploadDestination
gets passed to our custom provider. For this, we change plugin-upload/server/services/upload.js
. const entity = {
name: usedName,
alternativeText: fileInfo.alternativeText,
...
...
...
uploadDestination: fileInfo.uploadDestination
};
npx patch-package @strapi/plugin-upload
to create our patch-package file (complete patch file generated can be seen here).yarn build
and then run yarn develop
.