├── .gitignore ├── package.json ├── LICENSE ├── lib └── index.js └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | package-lock.json 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "strapi-provider-upload-oss", 3 | "version": "0.2.1", 4 | "description": "Aliyun OSS provider for strapi upload", 5 | "homepage": "http://strapi.io", 6 | "keywords": [ 7 | "upload", 8 | "aliyun", 9 | "oss", 10 | "strapi" 11 | ], 12 | "directories": { 13 | "lib": "./lib" 14 | }, 15 | "main": "./lib", 16 | "dependencies": { 17 | "ali-oss": "^6.0.1" 18 | }, 19 | "strapi": { 20 | "isProvider": true 21 | }, 22 | "author": { 23 | "email": "hezzze@gmail.com", 24 | "name": "hezzze", 25 | "url": "http://heze.io" 26 | }, 27 | "maintainers": [ 28 | { 29 | "name": "hezzze@gmail.com", 30 | "email": "hezzze", 31 | "url": "http://heze.io" 32 | } 33 | ], 34 | "repository": { 35 | "type": "git", 36 | "url": "git://github.com/hezzze/strapi-provider-upload-oss.git" 37 | }, 38 | "bugs": { 39 | "url": "https://github.com/hezzze/strapi-provider-upload-oss/issues" 40 | }, 41 | "engines": { 42 | "node": ">= 10.0.0", 43 | "npm": ">= 6.0.0" 44 | }, 45 | "license": "MIT" 46 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 hezzze 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const OSS = require('ali-oss'); 4 | 5 | const errLog = (...args) => { 6 | if (process.env.NODE_ENV !== 'production') { 7 | console.debug('>>>>>>> upload oss <<<<<<<'); 8 | console.debug(...args); 9 | } 10 | }; 11 | 12 | module.exports = { 13 | provider: 'aliyun-oss', 14 | name: 'Aliyun Web Service OSS', 15 | init: (config) => { 16 | const defaultBucketParams = { 17 | ACL: 'public-read', 18 | signedUrlExpires: 30 * 60, // 30 minutes 19 | } 20 | 21 | const ossClient = new OSS({ 22 | region: config.region, 23 | accessKeyId: config.accessKeyId, 24 | accessKeySecret: config.accessKeySecret, 25 | bucket: config.bucket, 26 | bucketParams: { 27 | ...defaultBucketParams, 28 | ...config.bucketParams, 29 | }, 30 | timeout: +(config.timeout * 1000), 31 | secure: !(config.secure === false), 32 | internal: config.internal === true, 33 | }); 34 | 35 | const getFileKey = (file) => { 36 | const path = file.path ? `${file.path}/` : ''; 37 | return `${config.uploadPath + '/' || ''}${path}${file.hash}${file.ext}`; 38 | }; 39 | 40 | const upload = (file, customParams = {}) => 41 | new Promise((resolve, reject) => { 42 | // upload file on OSS bucket 43 | const path = config.uploadPath ? `${config.uploadPath}/` : ''; 44 | const fileName = `${file.hash}${file.ext}`; 45 | const fullPath = `${path}${fileName}`; 46 | 47 | ossClient.put(fullPath, file.stream || Buffer.from(file.buffer, 'binary'), customParams) 48 | .then((result) => { 49 | if (config.baseUrl) { 50 | // use http protocol by default, but you can configure it as https protocol 51 | // deprecate config.domain, use baseUrl to specify protocol and domain. 52 | let baseUrl = config.baseUrl.replace(/\/$/, ''); 53 | let name = (result.name || '').replace(/^\//, ''); 54 | file.url = `${baseUrl}/${name}`; 55 | } else { 56 | file.url = result.url; 57 | } 58 | 59 | resolve(); 60 | }) 61 | .catch((err) => { 62 | reject(err); 63 | errLog(err); 64 | }); 65 | }); 66 | 67 | return { 68 | isPrivate() { 69 | return !!config.bucketParams && config.bucketParams.ACL === 'private'; 70 | }, 71 | async getSignedUrl(file, customParams = {}) { 72 | const fileKey = getFileKey(file); 73 | 74 | const url = await ossClient.asyncSignatureUrl( 75 | fileKey, 76 | customParams, 77 | ); 78 | 79 | return { url }; 80 | }, 81 | uploadStream(file, customParams = {}) { 82 | return upload(file, customParams); 83 | }, 84 | upload(file, customParams = {}) { 85 | return upload(file, customParams); 86 | }, 87 | delete(file, customParams = {}) { 88 | return new Promise((resolve, reject) => { 89 | // delete file on OSS bucket 90 | const path = config.uploadPath ? `${config.uploadPath}/` : ''; 91 | const fullPath = `${path}${file.hash}${file.ext}`; 92 | 93 | ossClient.delete(fullPath, customParams) 94 | .then((resp) => { 95 | if (resp.res && /2[0-9]{2}/.test(resp.res.statusCode)) { 96 | resolve(); 97 | } else { 98 | reject(new Error('OSS file deletion error')); 99 | } 100 | }) 101 | .catch((err) => { 102 | reject(err); 103 | }) 104 | }); 105 | }, 106 | }; 107 | }, 108 | }; 109 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # strapi-provider-upload-oss 2 | A provider for strapi server to upload file to Aliyun OSS. 3 | 4 | # Requirements 5 | - Node.js >= 10 6 | - npm > 6 7 | 8 | # Installation 9 | ```bash 10 | $ npm install strapi-provider-upload-oss --save 11 | ``` 12 | 13 | or 14 | 15 | ```bash 16 | $ yarn add strapi-provider-upload-oss --save 17 | ``` 18 | 19 | For more details, please see: https://strapi.io/documentation/developer-docs/latest/development/plugins/upload.html#using-a-provider 20 | 21 | # Usage 22 | 23 | 24 | ### Strapi v4 25 | 26 | The lastest version of the provider supports v4 by default, configuration is updated a little bit. See example below for ```./config/plugins.js```: 27 | 28 | ```javascript 29 | module.exports = ({ env }) => ({ 30 | upload: { 31 | config: { 32 | provider: 'strapi-provider-upload-oss', // full package name is required 33 | providerOptions: { 34 | accessKeyId: env('ACCESS_KEY_ID'), // required 35 | accessKeySecret: env('ACCESS_KEY_SECRET'), // required 36 | region: env('REGION'), // required 37 | bucket: env('BUCKET'), // required 38 | uploadPath: env('UPLOAD_PATH'), 39 | baseUrl: env('BASE_URL'), 40 | timeout: env('TIMEOUT'), 41 | secure: env('OSS_SECURE'), 42 | internal: env.bool('OSS_INTERNAL', false), 43 | bucketParams: { 44 | ACL: 'private', // default is 'public-read' 45 | signedUrlExpires: 60 * 60 // default is 30 * 60 (30min) 46 | } 47 | } 48 | } 49 | } 50 | }); 51 | ``` 52 | 53 | Official documentation [here](https://docs.strapi.io/developer-docs/latest/plugins/upload.html#enabling-the-provider) 54 | 55 | ### Strapi v3 56 | 57 | With a stable release of Strapi 3.0.0, the configuration was moved to a JavaScript file. Official documentation [here](https://docs-v3.strapi.io/developer-docs/latest/development/plugins/upload.html#enabling-the-provider). 58 | 59 | To enable the provider, create or edit the file at ```./config/plugins.js```. 60 | 61 | ```javascript 62 | module.exports = ({ env }) => ({ 63 | upload: { 64 | provider: 'oss', 65 | providerOptions: { 66 | accessKeyId: env('ACCESS_KEY_ID'), 67 | accessKeySecret: env('ACCESS_KEY_SECRET'), 68 | region: env('REGION'), 69 | bucket: env('BUCKET'), 70 | uploadPath: env('UPLOAD_PATH'), 71 | baseUrl: env('BASE_URL'), 72 | timeout: env('TIMEOUT'), 73 | secure: env('OSS_SECURE'), //default to true 74 | internal: env.bool('OSS_INTERNAL', false), 75 | } 76 | } 77 | }); 78 | ``` 79 | 80 | ### Provider Options 81 | 82 | Property | type | value 83 | ----- | ---- | ------------ 84 | **accessKeyId** | string | <aliyun access key id> 85 | **accessKeySecret** | string | <aliyun access key secret> 86 | **region** | string | OSS region (see reference below) 87 | **bucket** | string | bucket name 88 | **uploadPath** | string | path to store the file 89 | **baseUrl** | string | can be your custom oss url for accessing the uploaded file, e.g. //www.website.com 90 | **timeout** | integer | OSS upload timeout (unit: seconds) 91 | **secure** | boolean | will https mode be enabled for oss client 92 | **internal** | boolean | access OSS with aliyun internal network or not, default is false. If your servers are running on aliyun too, you can set true to save lot of money. 93 | 94 | 95 | # OSS Region reference 96 | https://help.aliyun.com/document_detail/31837.html#title-qvx-r3a-xr4 97 | 98 | # Troubleshooting 99 | 100 | Q: getting "The bucket you are attempting to access must be addressed using the specified endpoint. Please send all future requests to this endpoint." 101 | 102 | A: Check if the OSS region is correct for the bucket you're using 103 | 104 | # Contribution 105 | This repo is maintained periodically, any contribution is highly welcomed 106 | --------------------------------------------------------------------------------