├── .all-contributorsrc ├── .babelrc.js ├── .editorconfig ├── .eslintrc.js ├── .github └── badge.yml ├── .gitignore ├── .prettierignore ├── .prettierrc ├── .stylelintrc ├── .travis.yml ├── LICENSE ├── README-zh.md ├── README.md ├── build.sh ├── build └── rollup.config.js ├── docs ├── accept.md ├── basic.md ├── before-upload.md ├── draggable.md ├── faq.md ├── file.md ├── guide-ali-oss.md ├── guide-global-register.md ├── loaded.md ├── max.md ├── multiple.md ├── on-click.md ├── on-oversize.md ├── request.md ├── size.md ├── slot-default.md ├── slot-placeholder-spinner.md └── tip.md ├── netlify.sh ├── notify.sh ├── package.json ├── src ├── components │ ├── draggable-list.vue │ └── upload-item.vue ├── index.js ├── upload-to-ali.d.ts ├── upload-to-ali.vue └── utils.js ├── styleguide.config.js ├── test └── utils.test.js └── yarn.lock /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "projectName": "upload-to-ali", 3 | "projectOwner": "FEMessage", 4 | "repoType": "github", 5 | "repoHost": "https://github.com", 6 | "files": [ 7 | "README.md" 8 | ], 9 | "imageSize": 100, 10 | "commit": false, 11 | "commitConvention": "angular", 12 | "contributors": [ 13 | { 14 | "login": "levy9527", 15 | "name": "levy", 16 | "avatar_url": "https://avatars3.githubusercontent.com/u/9384365?v=4", 17 | "profile": "http://levy.work", 18 | "contributions": [ 19 | "code", 20 | "review", 21 | "infra", 22 | "blog", 23 | "ideas" 24 | ] 25 | }, 26 | { 27 | "login": "Alvin-Liu", 28 | "name": "Alvin", 29 | "avatar_url": "https://avatars0.githubusercontent.com/u/11909145?v=4", 30 | "profile": "https://github.com/Alvin-Liu", 31 | "contributions": [ 32 | "code", 33 | "review" 34 | ] 35 | }, 36 | { 37 | "login": "listars", 38 | "name": "listars", 39 | "avatar_url": "https://avatars2.githubusercontent.com/u/20613509?v=4", 40 | "profile": "https://github.com/listars", 41 | "contributions": [ 42 | "code", 43 | "review", 44 | "doc" 45 | ] 46 | }, 47 | { 48 | "login": "evillt", 49 | "name": "EVILLT", 50 | "avatar_url": "https://avatars3.githubusercontent.com/u/19513289?v=4", 51 | "profile": "https://evila.me", 52 | "contributions": [ 53 | "code", 54 | "doc" 55 | ] 56 | }, 57 | { 58 | "login": "donaldshen", 59 | "name": "Donald Shen", 60 | "avatar_url": "https://avatars3.githubusercontent.com/u/19591950?v=4", 61 | "profile": "https://donaldshen.github.io/portfolio", 62 | "contributions": [ 63 | "code", 64 | "doc", 65 | "test" 66 | ] 67 | }, 68 | { 69 | "login": "OuZuYu", 70 | "name": "OuZuYu", 71 | "avatar_url": "https://avatars3.githubusercontent.com/u/26338853?v=4", 72 | "profile": "http://67.216.223.155/resume/", 73 | "contributions": [ 74 | "bug" 75 | ] 76 | }, 77 | { 78 | "login": "xrr2016", 79 | "name": "轻剑快马", 80 | "avatar_url": "https://avatars1.githubusercontent.com/u/18013127?v=4", 81 | "profile": "https://justcodeit.fun", 82 | "contributions": [ 83 | "doc" 84 | ] 85 | }, 86 | { 87 | "login": "colmugx", 88 | "name": "ColMugX", 89 | "avatar_url": "https://avatars1.githubusercontent.com/u/21327913?v=4", 90 | "profile": "https://colmugx.github.io", 91 | "contributions": [ 92 | "bug" 93 | ] 94 | }, 95 | { 96 | "login": "rexerwang", 97 | "name": "Rexer Wang", 98 | "avatar_url": "https://avatars2.githubusercontent.com/u/15629940?v=4", 99 | "profile": "https://rexer.wang", 100 | "contributions": [ 101 | "bug" 102 | ] 103 | }, 104 | { 105 | "login": "cjfff", 106 | "name": "cjf", 107 | "avatar_url": "https://avatars1.githubusercontent.com/u/20502762?v=4", 108 | "profile": "http://www.ccc1996.cn", 109 | "contributions": [ 110 | "code", 111 | "doc" 112 | ] 113 | }, 114 | { 115 | "login": "Django-Tung", 116 | "name": "Django.Tung", 117 | "avatar_url": "https://avatars.githubusercontent.com/u/23208972?v=4", 118 | "profile": "https://github.com/Django-Tung", 119 | "contributions": [ 120 | "bug" 121 | ] 122 | } 123 | ], 124 | "contributorsPerLine": 7, 125 | "skipCi": true 126 | } 127 | -------------------------------------------------------------------------------- /.babelrc.js: -------------------------------------------------------------------------------- 1 | module.exports = api => { 2 | return { 3 | presets: [['@babel/env', {modules: api.env('test') ? 'commonjs' : false}]], 4 | plugins: [ 5 | [ 6 | '@babel/transform-runtime', 7 | { 8 | regenerator: true 9 | } 10 | ] 11 | ] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_style = space 8 | indent_size = 2 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | [*.md] 13 | insert_final_newline = false 14 | trim_trailing_whitespace = false 15 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | browser: true, 5 | node: true 6 | }, 7 | parserOptions: { 8 | parser: 'babel-eslint' 9 | }, 10 | extends: [ 11 | 'eslint:recommended', 12 | 'plugin:jest/recommended', 13 | 'plugin:vue/recommended', 14 | 'plugin:prettier/recommended', 15 | 'prettier/vue' 16 | ], 17 | plugins: ['vue', 'prettier', 'jest'], 18 | rules: { 19 | 'no-console': [ 20 | 'error', 21 | { 22 | allow: ['warn', 'error'] 23 | } 24 | ], 25 | 'no-debugger': 'error', 26 | 'prettier/prettier': 'error' 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /.github/badge.yml: -------------------------------------------------------------------------------- 1 | types: 2 | feat: 'enhancement' 3 | fix: 4 | hack: 'hack' 5 | default: 'bug' 6 | hack: 'hack' 7 | docs: 'documentation' 8 | refactor: 'refactor' 9 | style: 'style' 10 | test: 'test' 11 | perf: 'performance' 12 | chore: 13 | deps: 'dependencies' 14 | default: 'chore' 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | npm-debug.log* 4 | yarn-debug.log* 5 | yarn-error.log* 6 | dist 7 | docs/build 8 | docs/index.html 9 | 10 | # Editor directories and files 11 | .idea 12 | .vscode 13 | *.suo 14 | *.ntvs* 15 | *.njsproj 16 | *.sln 17 | .env 18 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | docs 2 | dist 3 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | semi: false 2 | singleQuote: true 3 | bracketSpacing: false 4 | -------------------------------------------------------------------------------- /.stylelintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "stylelint-config-standard", 3 | "rules": { 4 | "no-empty-source": null 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | branches: 2 | only: 3 | - master 4 | language: node_js 5 | node_js: 6 | - lts/* 7 | git: 8 | depth: 30 9 | install: 10 | - yarn --frozen-lockfile 11 | - yarn test 12 | script: 13 | - ./build.sh 14 | after_script: 15 | - ./notify.sh 16 | cache: yarn 17 | deploy: 18 | - provider: pages 19 | local-dir: docs 20 | github-token: $GITHUB_TOKEN 21 | skip-cleanup: true 22 | keep-history: true 23 | - provider: npm 24 | email: levy9527@qq.com 25 | api_key: $NPM_TOKEN 26 | skip-cleanup: true 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 FEMessage 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 | -------------------------------------------------------------------------------- /README-zh.md: -------------------------------------------------------------------------------- 1 | # upload-to-ali 2 | 3 | [![Build Status](https://badgen.net/travis/FEMessage/upload-to-ali/master)](https://travis-ci.com/FEMessage/upload-to-ali) 4 | [![NPM Download](https://badgen.net/npm/dm/@femessage/upload-to-ali)](https://www.npmjs.com/package/@femessage/upload-to-ali) 5 | [![NPM Version](https://badgen.net/npm/v/@femessage/upload-to-ali)](https://www.npmjs.com/package/@femessage/upload-to-ali) 6 | [![NPM License](https://badgen.net/npm/license/@femessage/upload-to-ali)](https://github.com/FEMessage/upload-to-ali/blob/master/LICENSE) 7 | [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/FEMessage/upload-to-ali/pulls) 8 | [![Automated Release Notes by gren](https://img.shields.io/badge/%F0%9F%A4%96-release%20notes-00B2EE.svg)](https://github-tools.github.io/github-release-notes/) 9 | 10 | 可通过环境变量配置上传信息,可自定义域名,支持多选、限制文件大小、删除、粘贴上传功能,拖拽上传功能,让上传功能更加简单。 11 | > 虽然最初的设计是上传至阿里云,但现在已可通过设置环境变量 `UPLOAD_ACTION` 把数据传送至自定义的后端接口,因此,理论上可以上传至任何云!考虑到有许多项目在用,就暂时不改包名了。 12 | 13 | ![](https://i.loli.net/2019/11/15/UZ2P7wR83GiDXky.gif) 14 | 15 | ## Table of Contents 16 | 17 | - [Feature](#feature) 18 | - [Links](#links) 19 | - [Install](#install) 20 | - [Config](#config) 21 | - [Dotenv](#dotenv) 22 | - [Contributing](#contributing) 23 | - [Contributors](#contributors) 24 | - [License](#license) 25 | 26 | ## Feature 27 | 28 | - 只需配置少量上传信息,即可实现上传功能 29 | - 上传前自动压缩图片,上传过程中有 loading 提示,支持图片显示及删除 30 | - 可拓展自定义 loading 和默认上传样式 31 | - 可限制上传文件大小和上传文件数量 32 | - 可截图粘贴上传 33 | - 可拖拽上传 34 | - 可预览图片 35 | - 支持 v-model 36 | 37 | 可以只设置 `action` props, 指向上传地址,组件内部默认实现了一套 post 方法,向上传地址传递数据 38 | 39 | 可以设置 `request`,实现自定义上传函数,覆盖原有默认的上传行为 40 | 41 | [⬆ Back to Top](#table-of-contents) 42 | 43 | ## Links 44 | 45 | - [docs](https://FEMessage.github.io/upload-to-ali/) 46 | - [ali oss guide](docs/guide-ali-oss.md) 47 | 48 | [⬆ Back to Top](#table-of-contents) 49 | 50 | ## Install 51 | 52 | ```sh 53 | yarn add @femessage/upload-to-ali 54 | ``` 55 | 56 | [⬆ Back to Top](#table-of-contents) 57 | 58 | ## Dotenv 59 | 60 | 推荐使用环境变量配置上传参数 61 | 62 | 使用 dotenv,我们只需要将环境变量配置写在`.env`文件中,配合 CI 工具,可满足同一套代码在不同环境对接不同上传配置的需求 63 | 64 | 以下是所有可传入的环境变量 65 | 66 | ```sh 67 | # .env文件 68 | # 以下所有参数都是可选的 69 | UPLOAD_ACTION=upload-url 70 | 71 | OSS_BUCKET=your-bucket 72 | OSS_REGION=oss-cn-beijing 73 | OSS_DIR=oss-dir 74 | OSS_CUSTOM_DOMAIN=cdn.xxx.com 75 | ``` 76 | 77 | `dotenv` 文档参考 https://www.npmjs.com/package/dotenv 78 | 79 | ### vue-cli3 80 | 81 | vue-cli3 提供了简便的方案替换[环境变量](https://cli.vuejs.org/zh/guide/mode-and-env.html#%E6%A8%A1%E5%BC%8F),但无法在客户端注入。这个场景需要结合`dotenv-webpack`插件。 82 | 83 | ```js 84 | // vue.config.js 85 | const Dotenv = require('dotenv-webpack') 86 | module.exports = { 87 | configureWebpack: { 88 | plugins: [new Dotenv()] 89 | } 90 | } 91 | ``` 92 | 93 | [⬆ Back to Top](#table-of-contents) 94 | 95 | ## Contributing 96 | 97 | For those who are interested in contributing to this project, such as: 98 | 99 | - report a bug 100 | - request new feature 101 | - fix a bug 102 | - implement a new feature 103 | 104 | Please refer to our [contributing guide](https://github.com/FEMessage/.github/blob/master/CONTRIBUTING.md). 105 | 106 | [⬆ Back to Top](#table-of-contents) 107 | 108 | ## Contributors 109 | 110 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): 111 | 112 | 113 | 114 | 115 |
levy
levy

💻 👀 🚇 📝 🤔
Alvin
Alvin

💻 👀
listars
listars

💻 👀 📖
EVILLT
EVILLT

💻 📖
Donald Shen
Donald Shen

💻 📖
OuZuYu
OuZuYu

🐛
轻剑快马
轻剑快马

📖
ColMugX
ColMugX

🐛
Rexer Wang
Rexer Wang

🐛
116 | 117 | 118 | 119 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! 120 | 121 | ## License 122 | 123 | [MIT](./LICENSE) 124 | 125 | [⬆ Back to Top](#table-of-contents) 126 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # upload-to-ali 2 | 3 | [![Build Status](https://badgen.net/travis/FEMessage/upload-to-ali/master)](https://travis-ci.com/FEMessage/upload-to-ali) 4 | [![NPM Download](https://badgen.net/npm/dm/@femessage/upload-to-ali)](https://www.npmjs.com/package/@femessage/upload-to-ali) 5 | [![NPM Version](https://badgen.net/npm/v/@femessage/upload-to-ali)](https://www.npmjs.com/package/@femessage/upload-to-ali) 6 | [![NPM License](https://badgen.net/npm/license/@femessage/upload-to-ali)](https://github.com/FEMessage/upload-to-ali/blob/master/LICENSE) 7 | [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/FEMessage/upload-to-ali/pulls) 8 | [![Automated Release Notes by gren](https://img.shields.io/badge/%F0%9F%A4%96-release%20notes-00B2EE.svg)](https://github-tools.github.io/github-release-notes/) 9 | 10 | In the beginning, this component is designed to upload file to Aliyun-OSS easily,but now it can upload to any oss as you wish. 11 | 12 | You can configure OSS information through environmental variables, customize domain, support multi-selection, limit file size, delete, paste to upload, drag and drop to upload, make files upload simpler. 13 | 14 | ![](https://i.loli.net/2019/11/15/UZ2P7wR83GiDXky.gif) 15 | 16 | [中文文档](./README-zh.md) 17 | 18 | ## Table of Contents 19 | 20 | - [Feature](#feature) 21 | - [Links](#links) 22 | - [Install](#install) 23 | - [Config](#config) 24 | - [Dotenv](#dotenv) 25 | - [Contributing](#contributing) 26 | - [Contributors](#contributors) 27 | - [License](#license) 28 | 29 | ## Feature 30 | 31 | - The upload function can be done with little oss configuration 32 | - Automatically compress pictures before uploading, and loading prompts during uploading, support picture display and deletion 33 | - With default styles and support customize 34 | - Can limit the size or the number of files to upload 35 | - Support paste screenshot to upload 36 | - Can drag and drop to upload 37 | - Can preivew img 38 | - support v-model 39 | 40 | You can only set `action` props, that refers to upload url, the component has a default implement to post data to the url. 41 | 42 | You can set `request` props to customize own upload function. 43 | 44 | [⬆Back to Top](#table-of-contents) 45 | 46 | ## Links 47 | 48 | - [docs](https://FEMessage.github.io/upload-to-ali/) 49 | - [ali oss guide](docs/guide-ali-oss.md) 50 | 51 | [⬆ Back to Top](#table-of-contents) 52 | 53 | ## Install 54 | 55 | ```bash 56 | yarn add @femessage/upload-to-ali 57 | ``` 58 | 59 | [⬆Back to Top](#table-of-contents) 60 | 61 | ## Dotenv 62 | 63 | Recommend using environment variables to configure upload parameters
With dotenv, we just need to write the environment variable in `.env`. With CI tools, this can meet the needs of using different configuration in different environments without change the source code.
Here are all can passed-in environment variables 64 | 65 | ```sh 66 | # .env file 67 | # these params are all optional 68 | UPLOAD_ACTION=upload-url 69 | 70 | OSS_BUCKET=your-bucket 71 | OSS_REGION=oss-cn-beijing 72 | OSS_DIR=oss-dir 73 | OSS_CUSTOM_DOMAIN=cdn.xxx.com 74 | ``` 75 | 76 | `dotenv` document reference [https://www.npmjs.com/package/dotenv](https://www.npmjs.com/package/dotenv) 77 | 78 | ### vue-cli3 79 | 80 | vue-cli3 offers an easy solution to replace [process.env](https://cli.vuejs.org/zh/guide/mode-and-env.html#%E6%A8%A1%E5%BC%8F), but it requires a pattern(VUE*APP*\*) to inject in client side. So we need to use `dotenv-webpack`'s solution. 81 | 82 | ```js 83 | // vue.config.js 84 | const Dotenv = require('dotenv-webpack') 85 | module.exports = { 86 | configureWebpack: { 87 | plugins: [new Dotenv()] 88 | } 89 | } 90 | ``` 91 | 92 | [⬆Back to Top](#table-of-contents) 93 | 94 | ## Contributing 95 | 96 | For those who are interested in contributing to this project, such as: 97 | 98 | - report a bug 99 | - request new feature 100 | - fix a bug 101 | - implement a new feature 102 | 103 | Please refer to our [contributing guide](https://github.com/FEMessage/.github/blob/master/CONTRIBUTING.md). 104 | 105 | [⬆ Back to Top](#table-of-contents) 106 | 107 | ## Contributors 108 | 109 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 |

levy

💻 👀 🚇 📝 🤔

Alvin

💻 👀

listars

💻 👀 📖

EVILLT

💻 📖

Donald Shen

💻 📖 ⚠️

OuZuYu

🐛

轻剑快马

📖

ColMugX

🐛

Rexer Wang

🐛

cjf

💻 📖

Django.Tung

🐛
131 | 132 | 133 | 134 | 135 | 136 | 137 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! 138 | 139 | ## License 140 | 141 | [MIT](./LICENSE) 142 | 143 | [⬆ Back to Top](#table-of-contents) 144 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | yarn stdver 3 | 4 | yarn build 5 | -------------------------------------------------------------------------------- /build/rollup.config.js: -------------------------------------------------------------------------------- 1 | // rollup.config.js 2 | import vue from 'rollup-plugin-vue' 3 | import babel from 'rollup-plugin-babel' 4 | import commonjs from 'rollup-plugin-commonjs' 5 | import {terser} from 'rollup-plugin-terser' 6 | import minimist from 'minimist' 7 | 8 | const argv = minimist(process.argv.slice(2)) 9 | 10 | const config = { 11 | input: 'src/index.js', 12 | output: { 13 | name: 'UploadToAli', 14 | exports: 'named' 15 | }, 16 | plugins: [ 17 | commonjs(), 18 | vue({ 19 | css: true, 20 | compileTemplate: true 21 | }), 22 | babel({ 23 | runtimeHelpers: true, 24 | exclude: 'node_modules/**' 25 | }) 26 | ] 27 | } 28 | 29 | // Only minify browser (iife) version 30 | if (argv.format === 'iife') { 31 | config.plugins.push(terser()) 32 | } 33 | 34 | export default config 35 | -------------------------------------------------------------------------------- /docs/accept.md: -------------------------------------------------------------------------------- 1 | 自定义上传文件类型 2 | 3 | ```vue 4 | 7 | 16 | ``` 17 | -------------------------------------------------------------------------------- /docs/basic.md: -------------------------------------------------------------------------------- 1 | 基本用法。内部使用`img-preview`组件进行图片预览 2 | 3 | ```vue 4 | 7 | 16 | ``` 17 | -------------------------------------------------------------------------------- /docs/before-upload.md: -------------------------------------------------------------------------------- 1 | 使用 beforeUpload 自定义上传前检查。比如检查图片的尺寸。 2 | Promise.reject()阻止上传,返回Promise.resolve()则可以上传 3 | 4 | ```vue 5 | 8 | 34 | ``` 35 | -------------------------------------------------------------------------------- /docs/draggable.md: -------------------------------------------------------------------------------- 1 | 可以拖拽排序 2 | 3 | ```vue 4 | 7 | 21 | ``` 22 | -------------------------------------------------------------------------------- /docs/faq.md: -------------------------------------------------------------------------------- 1 | ## 在 TypeScript 中指定组件的类型 2 | 3 | ```html 4 | 7 | 21 | ``` 22 | -------------------------------------------------------------------------------- /docs/file.md: -------------------------------------------------------------------------------- 1 | 上传文件 2 | 3 | ```vue 4 | 7 | 18 | ``` 19 | -------------------------------------------------------------------------------- /docs/guide-ali-oss.md: -------------------------------------------------------------------------------- 1 | # 阿里云OSS配置指南 2 | 3 | 对于新建的bucket,需要做一些设置 4 | 9 | 10 | ### 绑定域名 11 | 12 | 13 | 假设用户域名为:static.deepexi.top
14 | 15 | 16 | 则使用自定义域名访问,可以解决html变成下载的问题 17 | 18 | ### CNAME设置 19 | 如果绑定的是同一个阿里云账号下的域名,则可以自动添加CNAME记录。否则需要手动添加。 20 | 21 | 查看bucket外网地址:deepexi-serverless.oss-cn-shenzhen.aliyuncs.com
22 | 23 | 24 | 则去域名解析供应商设置: 25 | 26 | static.deepexi.top   -> CNAME -> deepexi-serverless.oss-cn-shenzhen.aliyuncs.com 27 | 28 | 29 | ### 证书托管 30 | 31 | 上传HTTPS证书,开启HTTPS 32 | 33 | 34 | 35 | 36 | 37 | 如果没有证书,查看教程获取:[🔒免费开启HTTPS](https://github.com/levy9527/blog/issues/5) 38 | 39 | ### 公共读 40 | 41 | 点击基础设置,设置读写权限为公共读
42 | 43 | 44 | 这样可以解决访问链接超时的问题 45 | 46 | ### 跨域设置 47 | 在基础设置下,找到跨域设置 48 | 49 | 50 | 51 | 在来源中设置域名,或ip地址。下面给出最简单的示例为 `*`,实际可以根据需要填写允许的域名,一行一个。 52 | 53 | - 将allowed origins设置成 `*` 54 | - 将allowed methods设置成`GET, POST, PUT, DELETE, HEAD` 55 | - 将allowed headers设置成 `*` 56 | - 将expose headers设置成  57 | - `etag` 58 | - `x-oss-request-id` 59 | 60 | 这样可以解决字体无法显示的问题 61 | -------------------------------------------------------------------------------- /docs/guide-global-register.md: -------------------------------------------------------------------------------- 1 | 如果组件在项目中多个地方被使用的话,每次指定 `request` 方法是比较烦琐的一件事情。 2 | 3 | 可以用两种方式去进行全局注册 `request` 方法 4 | 5 | 1.全局注册组件 6 | 7 | ```md 8 | import Vue from 'vue'; 9 | import UploadToAli from 'upload-to-ali' 10 | 11 | Vue.use(UploadToAli, { 12 | // 回调的文件 13 | async request(file) { 14 | const url = await customRequest(file) 15 | return url; 16 | } 17 | }) 18 | ``` 19 | 20 | 2. 在 Vue 的原型链上添加 `$uploadRequest` 方法 21 | 22 | ```md 23 | Vue.prototype.$uploadRequest = async request(file) { 24 | const url = await customRequest(file) 25 | return url; 26 | } 27 | ``` 28 | -------------------------------------------------------------------------------- /docs/loaded.md: -------------------------------------------------------------------------------- 1 | 上传回调 2 | 3 | 上传后的文件 url 在`@loaded`事件会返回 4 | 5 | ```vue 6 | 9 | 24 | ``` 25 | -------------------------------------------------------------------------------- /docs/max.md: -------------------------------------------------------------------------------- 1 | 限制文件数量 2 | 3 | ```vue 4 | 7 | 16 | ``` 17 | -------------------------------------------------------------------------------- /docs/multiple.md: -------------------------------------------------------------------------------- 1 | 上传多张操作 2 | 3 | ```vue 4 | 7 | 16 | ``` 17 | -------------------------------------------------------------------------------- /docs/on-click.md: -------------------------------------------------------------------------------- 1 | 自定义事件 2 | 3 | ```vue 4 | 7 | 21 | ``` 22 | -------------------------------------------------------------------------------- /docs/on-oversize.md: -------------------------------------------------------------------------------- 1 | 可以自定义如何处理文件大小超出的情况(默认是alert警告) 2 | 3 | ```vue 4 | 7 | 21 | ``` 22 | -------------------------------------------------------------------------------- /docs/request.md: -------------------------------------------------------------------------------- 1 | 覆盖默认的上传行为,可以自定义上传的实现 2 | 3 | ```vue 4 | 7 | 8 | 26 | ``` 27 | -------------------------------------------------------------------------------- /docs/size.md: -------------------------------------------------------------------------------- 1 | 限制文件大小,size单位为kb 2 | 3 | ```vue 4 | 7 | 16 | ``` 17 | -------------------------------------------------------------------------------- /docs/slot-default.md: -------------------------------------------------------------------------------- 1 | 自定义上传内容 2 | 3 | 下面是一个真实遇到过的例子:上传组件被包在 form 元素里,上传组件自定义上传内容为 button, 此时需要设置 button 的 type="button",否则点击按钮后会触发表单的提交。 4 | 5 | ```vue 6 | 13 | 28 | ``` 29 | -------------------------------------------------------------------------------- /docs/slot-placeholder-spinner.md: -------------------------------------------------------------------------------- 1 | 自定义上传占位符和 loading 图标 2 | 3 | ```vue 4 | 12 | 21 | ``` 22 | -------------------------------------------------------------------------------- /docs/tip.md: -------------------------------------------------------------------------------- 1 | 自定义上传提示内容 2 | 3 | ```vue 4 | 7 | 16 | ``` 17 | -------------------------------------------------------------------------------- /netlify.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | echo "is netlify: $NETLIFY" 3 | echo "in branch: $BRANCH" 4 | echo "head: $HEAD" 5 | 6 | if [ "$NETLIFY" != "true" ] 7 | then 8 | echo "this script only runs in netlify, bye" 9 | exit 1 10 | fi 11 | 12 | if [ "$BRANCH" != "dev" ] && [ "$HEAD" != "dev" ] 13 | then 14 | yarn doc 15 | else 16 | echo "this script only runs in targeting dev's PR deploy preview, bye" 17 | fi 18 | -------------------------------------------------------------------------------- /notify.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # https://stackoverflow.com/questions/13872048/bash-script-what-does-bin-bash-mean 3 | echo "1/5: checking TRAVIS_TEST_RESULT" 4 | if [ "$TRAVIS_TEST_RESULT" != "0" ] 5 | then 6 | echo "build not success, bye" 7 | exit 1 8 | fi 9 | 10 | ORG_NAME=$(echo "$TRAVIS_REPO_SLUG" | cut -d '/' -f 1) 11 | REPO_NAME=$(echo "$TRAVIS_REPO_SLUG" | cut -d '/' -f 2) 12 | 13 | echo "2/5: pushing commit and tag to github" 14 | # 该命令很可能报错,但不影响实际进行,因而不能简单地在脚本开头 set -e 15 | git remote add github https://$GITHUB_TOKEN@github.com/$TRAVIS_REPO_SLUG.git > /dev/null 2>&1 16 | git push github HEAD:master --follow-tags 17 | 18 | echo "3/5: generating github release notes" 19 | GREN_GITHUB_TOKEN=$GITHUB_TOKEN yarn release 20 | 21 | # 避免发送错误信息 22 | if [ $? -ne 0 ] 23 | then 24 | echo "gren fails, bye" 25 | exit 1 26 | fi 27 | 28 | echo "4/5: downloading github release info" 29 | url=https://api.github.com/repos/$TRAVIS_REPO_SLUG/releases/latest 30 | resp_tmp_file=resp.tmp 31 | 32 | curl -H "Authorization: token $GITHUB_TOKEN" $url > $resp_tmp_file 33 | 34 | html_url=$(sed -n 5p $resp_tmp_file | sed 's/\"html_url\"://g' | awk -F '"' '{print $2}') 35 | body=$(grep body < $resp_tmp_file | sed 's/\"body\"://g;s/\"//g') 36 | version=$(echo $html_url | awk -F '/' '{print $NF}') 37 | 38 | echo "5/5: notifying with dingtalk bot" 39 | msg='{"msgtype": "markdown", "markdown": {"title": "'$REPO_NAME'更新", "text": "@所有人\n# ['$REPO_NAME'('$version')]('$html_url')\n'$body'"}}' 40 | 41 | curl -X POST https://oapi.dingtalk.com/robot/send\?access_token\=$DINGTALK_ROBOT_TOKEN -H 'Content-Type: application/json' -d "$msg" 42 | 43 | rm $resp_tmp_file 44 | 45 | echo "executing notify.sh successfully" 46 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@femessage/upload-to-ali", 3 | "version": "1.5.0", 4 | "description": "", 5 | "author": "https://github.com/FEMessage", 6 | "license": "MIT", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/FEMessage/upload-to-ali.git" 10 | }, 11 | "keywords": [ 12 | "vue", 13 | "upload", 14 | "component" 15 | ], 16 | "files": [ 17 | "src", 18 | "dist" 19 | ], 20 | "main": "dist/upload-to-ali.umd.js", 21 | "module": "dist/upload-to-ali.esm.js", 22 | "unpkg": "dist/upload-to-ali.min.js", 23 | "browser": { 24 | "./sfc": "src/upload-to-ali.vue" 25 | }, 26 | "types": "src/upload-to-ali.d.ts", 27 | "scripts": { 28 | "dev": "vue-styleguidist server", 29 | "test": "jest", 30 | "doc": "vue-styleguidist build", 31 | "build": "npm run build:unpkg & npm run build:es & npm run build:umd & npm run doc", 32 | "build:umd": "rollup --config build/rollup.config.js --format umd --file dist/upload-to-ali.umd.js", 33 | "build:es": "rollup --config build/rollup.config.js --format es --file dist/upload-to-ali.esm.js", 34 | "build:unpkg": "rollup --config build/rollup.config.js --format iife --file dist/upload-to-ali.min.js", 35 | "stdver": "standard-version -m '[skip ci] chore(release): v%s'", 36 | "release": "gren release --override" 37 | }, 38 | "dependencies": { 39 | "@femessage/img-preview": "^1.2.0", 40 | "compressorjs": "^1.0.6", 41 | "crypto-browserify": "^3.12.0" 42 | }, 43 | "devDependencies": { 44 | "@babel/core": "^7.4.3", 45 | "@babel/plugin-transform-runtime": "^7.4.3", 46 | "@babel/preset-env": "^7.4.3", 47 | "@femessage/github-release-notes": "latest", 48 | "babel-eslint": "^10.0.3", 49 | "babel-loader": "^8.0.5", 50 | "dotenv": "^7.0.0", 51 | "eslint": "^6.6.0", 52 | "eslint-config-prettier": "^6.5.0", 53 | "eslint-plugin-jest": "^23.1.1", 54 | "eslint-plugin-prettier": "^3.1.1", 55 | "eslint-plugin-vue": "^5.2.3", 56 | "file-loader": "^3.0.1", 57 | "glob": "^7.1.3", 58 | "husky": "1.3.1", 59 | "jest": "^24.8.0", 60 | "less": "^3.9.0", 61 | "less-loader": "^5.0.0", 62 | "lint-staged": "^8.1.0", 63 | "minimist": "^1.2.0", 64 | "prettier": "1.18.2", 65 | "rollup": "^1.9.0", 66 | "rollup-plugin-babel": "^4.3.2", 67 | "rollup-plugin-commonjs": "^9.3.4", 68 | "rollup-plugin-terser": "^4.0.4", 69 | "rollup-plugin-vue": "^4.7.2", 70 | "standard-version": "^6.0.1", 71 | "stylelint": "^9.10.0", 72 | "stylelint-config-standard": "^18.2.0", 73 | "vue": "^2.6.10", 74 | "vue-loader": "^15.7.1", 75 | "vue-styleguidist": "^3.16.3", 76 | "vue-template-compiler": "^2.5.16", 77 | "webpack": "^4.29.6" 78 | }, 79 | "publishConfig": { 80 | "access": "public" 81 | }, 82 | "vue-sfc-cli": "1.12.0", 83 | "engines": { 84 | "node": ">= 4.0.0", 85 | "npm": ">= 3.0.0" 86 | }, 87 | "husky": { 88 | "hooks": { 89 | "pre-commit": "lint-staged", 90 | "post-commit": "git update-index --again", 91 | "pre-push": "yarn test" 92 | } 93 | }, 94 | "lint-staged": { 95 | "*.@(md|json)": [ 96 | "prettier --write", 97 | "git add" 98 | ], 99 | "*.js": [ 100 | "eslint --fix", 101 | "prettier --write", 102 | "git add" 103 | ], 104 | "*.vue": [ 105 | "eslint --fix", 106 | "prettier --write", 107 | "stylelint --fix", 108 | "git add" 109 | ] 110 | }, 111 | "gren": "@femessage/grenrc" 112 | } 113 | -------------------------------------------------------------------------------- /src/components/draggable-list.vue: -------------------------------------------------------------------------------- 1 | 7 | 62 | 76 | -------------------------------------------------------------------------------- /src/components/upload-item.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 38 | 39 | 64 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | // Import vue component 2 | import Component from './upload-to-ali.vue' 3 | 4 | // `Vue.use` automatically prevents you from using 5 | // the same plugin more than once, 6 | // so calling it multiple times on the same plugin 7 | // will install the plugin only once 8 | Component.install = (Vue, opts = {}) => { 9 | if (opts.request) { 10 | Vue.prototype.$uploadRequest = opts.request 11 | } 12 | Vue.component(Component.name, Component) 13 | } 14 | 15 | // To auto-install when vue is found 16 | let GlobalVue = null 17 | if (typeof window !== 'undefined') { 18 | GlobalVue = window.Vue 19 | } else if (typeof global !== 'undefined') { 20 | GlobalVue = global.Vue 21 | } 22 | if (GlobalVue) { 23 | GlobalVue.use(Component) 24 | } 25 | 26 | // To allow use as module (npm/webpack/etc.) export component 27 | export default Component 28 | 29 | // It's possible to expose named exports when writing components that can 30 | // also be used as directives, etc. - eg. import { RollupDemoDirective } from 'rollup-demo'; 31 | // export const RollupDemoDirective = component; 32 | -------------------------------------------------------------------------------- /src/upload-to-ali.d.ts: -------------------------------------------------------------------------------- 1 | import Vue, {VueConstructor} from 'vue' 2 | 3 | declare module '@femessage/upload-to-ali' { 4 | class FemessageComponent extends Vue { 5 | static install(vue: typeof Vue): void 6 | } 7 | 8 | type CombinedVueInstance< 9 | Instance extends Vue, 10 | Data, 11 | Methods, 12 | Computed, 13 | Props 14 | > = Data & Methods & Computed & Props & Instance 15 | 16 | type ExtendedVue< 17 | Instance extends Vue, 18 | Data, 19 | Methods, 20 | Computed, 21 | Props 22 | > = VueConstructor< 23 | CombinedVueInstance & Vue 24 | > 25 | 26 | type Combined = Data & 27 | Methods & 28 | Computed & 29 | Props 30 | 31 | type UploadToAliData = { 32 | previewUrl: string 33 | 34 | uploading: boolean 35 | 36 | isHighlight: boolean 37 | } 38 | 39 | type UploadToAliMethods = { 40 | selectFiles: () => void 41 | } 42 | 43 | type UploadToAliComputed = { 44 | uploadList: any[] 45 | canUpload: boolean 46 | uploadRequest: (file: any) => Promise 47 | } 48 | 49 | type UploadToAliProps = { 50 | action: string 51 | bucket: string 52 | region: string 53 | dir: string 54 | customDomain: string 55 | value: string | any[] 56 | multiple: boolean 57 | size: number 58 | accept: string 59 | timeout: number 60 | disabled: boolean 61 | max: number 62 | compressOptions: {[key: string]: any} 63 | uploadOptions: {[key: string]: any} 64 | preview: boolean 65 | tip: string 66 | onClick: (url: string, isFile: boolean) => void 67 | beforeUpload: (files: any[]) => Promise 68 | onOversize: (fileOvesize: any) => void 69 | request: (file: any) => Promise 70 | } 71 | 72 | type UploadToAli = Combined< 73 | UploadToAliData, 74 | UploadToAliMethods, 75 | UploadToAliComputed, 76 | UploadToAliProps 77 | > 78 | 79 | export interface UploadToAliType extends FemessageComponent, UploadToAli {} 80 | 81 | const UploadToAliConstruction: ExtendedVue< 82 | Vue, 83 | UploadToAliData, 84 | UploadToAliMethods, 85 | UploadToAliComputed, 86 | UploadToAliProps 87 | > 88 | 89 | export default UploadToAliConstruction 90 | } 91 | -------------------------------------------------------------------------------- /src/upload-to-ali.vue: -------------------------------------------------------------------------------- 1 | 85 | 86 | 484 | 653 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | import crypto from 'crypto-browserify' 2 | 3 | /** 4 | * 签名函数参考 5 | * https://help.aliyun.com/document_detail/29442.html?spm=a2c4g.11186623.2.13.2c541605Ky1bxj 6 | */ 7 | export function getSignature(origin, timestamp) { 8 | const param = { 9 | origin, 10 | timestamp, 11 | signatureMethod: 'HMAC-SHA1' 12 | } 13 | const paramStr = Object.keys(param) 14 | .sort() 15 | .map(k => `${k}=${encodeURIComponent(param[k])}`) 16 | .join('&') 17 | const signStr = 'POST&%2F&' + encodeURIComponent(paramStr) 18 | 19 | return crypto 20 | .createHmac('sha1', 'nonce') 21 | .update(signStr) 22 | .digest('base64') 23 | } 24 | 25 | export function getBasename(url = '') { 26 | const filename = decodeURIComponent(url.split('/').pop()) 27 | return filename.length > 40 ? `${filename.slice(0, 40)}...` : filename 28 | } 29 | /** 30 | * 默认上传方法 31 | * @param {*} file 32 | */ 33 | export function defaultRequest(file) { 34 | const formData = new FormData() 35 | ;['bucket', 'region', 'customDomain', 'dir'] 36 | .filter(key => this[key]) 37 | .forEach(key => formData.append(key, this[key])) 38 | formData.append('file', file) 39 | 40 | return new Promise((resolve, reject) => { 41 | if (!this.action) { 42 | return reject(new Error('missing UPLOAD_ACTION')) 43 | } 44 | const xhr = new XMLHttpRequest() 45 | xhr.responseType = 'json' 46 | xhr.onload = () => { 47 | if (xhr.status === 200) { 48 | resolve(xhr.response.payload.url) 49 | } else { 50 | reject(xhr.response) 51 | } 52 | } 53 | xhr.onerror = reject 54 | const timestamp = Date.now() 55 | const sep = this.action.indexOf('?') > -1 ? '&' : '?' 56 | const url = `${this.action}${sep}_=${timestamp}` 57 | xhr.open('POST', url, true) 58 | 59 | const signature = getSignature(location.origin, timestamp) 60 | xhr.setRequestHeader('x-upload-timestamp', timestamp) 61 | xhr.setRequestHeader('x-upload-signature', signature) 62 | 63 | xhr.send(formData) 64 | }) 65 | } 66 | -------------------------------------------------------------------------------- /styleguide.config.js: -------------------------------------------------------------------------------- 1 | const {VueLoaderPlugin} = require('vue-loader') 2 | const path = require('path') 3 | const glob = require('glob') 4 | const env = Object.assign({}, require('dotenv').config().parsed, { 5 | UPLOAD_ACTION: process.env.UPLOAD_ACTION, 6 | OSS_BUCKET: process.env.OSS_BUCKET, 7 | OSS_REGION: process.env.OSS_REGION 8 | }) 9 | 10 | const sections = (() => { 11 | const docs = glob 12 | .sync('docs/*.md') 13 | .map(p => ({name: path.basename(p, '.md'), content: p})) 14 | const demos = [] 15 | let faq = '' // 约定至多只有一个faq.md 16 | const guides = [] 17 | docs.forEach(d => { 18 | if (/^faq$/i.test(d.name)) { 19 | d.name = d.name.toUpperCase() 20 | faq = d 21 | } else if (/^guide-/.test(d.name)) { 22 | guides.push(d) 23 | } else { 24 | demos.push(d) 25 | } 26 | }) 27 | return [ 28 | { 29 | name: 'Components', 30 | components: 'src/*.vue', 31 | usageMode: 'expand' 32 | }, 33 | { 34 | name: 'Demo', 35 | sections: demos, 36 | sectionDepth: 2 37 | }, 38 | ...(faq ? [faq] : []), 39 | ...(guides.length ? [{name: 'Guide', sections: guides}] : []) 40 | ] 41 | })() 42 | 43 | module.exports = { 44 | styleguideDir: 'docs', 45 | pagePerSection: true, 46 | ribbon: { 47 | url: 'https://github.com/FEMessage/upload-to-ali' 48 | }, 49 | sections, 50 | editorConfig: { 51 | readOnly: process.env.NODE_ENV === 'development' ? false : 'nocursor' 52 | }, 53 | webpackConfig: { 54 | module: { 55 | rules: [ 56 | { 57 | test: /\.vue$/, 58 | loader: 'vue-loader' 59 | }, 60 | { 61 | test: /\.js?$/, 62 | exclude: /node_modules/, 63 | loader: 'babel-loader' 64 | }, 65 | { 66 | test: /\.css$/, 67 | loaders: ['style-loader', 'css-loader'] 68 | }, 69 | { 70 | test: /\.less$/, 71 | loaders: ['vue-style-loader', 'css-loader', 'less-loader'] 72 | }, 73 | { 74 | test: /\.(woff2?|eot|[ot]tf)(\?.*)?$/, 75 | loader: 'file-loader' 76 | } 77 | ] 78 | }, 79 | plugins: [ 80 | new VueLoaderPlugin(), 81 | new (require('webpack')).DefinePlugin({ 82 | 'process.env': JSON.stringify(env) 83 | }) 84 | ] 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /test/utils.test.js: -------------------------------------------------------------------------------- 1 | import {getBasename, getSignature} from '../src/utils' 2 | 3 | describe('getBasename', () => { 4 | test('仅存在文件名', () => { 5 | const str = 'README.md' 6 | expect(getBasename(str)).toBe('README.md') 7 | }) 8 | 9 | test('小于 40 字符文件地址', () => { 10 | const str = '//localhost/all-contributorsrc' 11 | expect(getBasename(str)).toBe('all-contributorsrc') 12 | }) 13 | 14 | test('大于 40 字符中英文混合文件地址', () => { 15 | const fileName = '苹果apple香蕉banana西瓜watermelon桃子peach柠檬lemon.md' 16 | const str = `//localhost/${fileName}` 17 | expect(getBasename(str)).toBe(`${fileName.slice(0, 40)}...`) 18 | }) 19 | 20 | test('文件名被 encode 的文件地址', () => { 21 | const str2 = '//localhost/example%2B1.png' 22 | expect(getBasename(str2)).toBe('example+1.png') 23 | }) 24 | }) 25 | 26 | describe('getSignature', () => { 27 | test('用例一', () => { 28 | const origin = 'http://localhost:6060' 29 | const timestamp = 1575362612539 30 | expect(getSignature(origin, timestamp)).toBe('VBrn5MJFxMj5OPExLx8eXq1DCCc=') 31 | }) 32 | test('用例二', () => { 33 | const origin = 'https://example.com' 34 | const timestamp = 1575362612539 35 | expect(getSignature(origin, timestamp)).toBe('iZj/mMZghtdi1jyk9zqG8mYiKPo=') 36 | }) 37 | }) 38 | --------------------------------------------------------------------------------