├── .gitignore
├── hybrid
└── html
│ ├── README.md
│ ├── js
│ ├── signclient.js
│ └── h5uploader.js
│ └── index.html
├── static
└── logo.png
├── main.js
├── App.vue
├── .hbuilderx
└── launch.json
├── pages
├── index
│ ├── index.vue
│ ├── fileUpload.vue
│ ├── h5Upload.vue
│ └── androidUpload.vue
└── yzcloud
│ └── yz-preview.vue
├── config
└── index.js
├── pages.json
├── uni.scss
├── README.md
└── manifest.json
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | .project
3 | unpackage/
4 | .vscode/
5 | .idea
6 | .DS_Store
7 |
--------------------------------------------------------------------------------
/hybrid/html/README.md:
--------------------------------------------------------------------------------
1 | ## web-view加载本地html
2 | > 参考https://uniapp.dcloud.io/component/web-view
--------------------------------------------------------------------------------
/static/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lavieAll/uniapp-file-upload/HEAD/static/logo.png
--------------------------------------------------------------------------------
/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import App from './App'
3 |
4 | Vue.config.productionTip = false
5 |
6 | App.mpType = 'app'
7 |
8 | const app = new Vue({
9 | ...App
10 | })
11 | app.$mount()
12 |
--------------------------------------------------------------------------------
/App.vue:
--------------------------------------------------------------------------------
1 |
14 |
15 |
18 |
--------------------------------------------------------------------------------
/.hbuilderx/launch.json:
--------------------------------------------------------------------------------
1 | { // launch.json 配置了启动调试时相关设置,configurations下节点名称可为 app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/
2 | // launchtype项可配置值为local或remote, local代表前端连本地云函数,remote代表前端连云端云函数
3 | "version": "0.0",
4 | "configurations": [{
5 | "type": "uniCloud",
6 | "default": {
7 | "launchtype": "remote"
8 | }
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/pages/index/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
24 |
--------------------------------------------------------------------------------
/pages/yzcloud/yz-preview.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 文档地址无效,无法加载该文档
5 |
6 |
7 |
8 |
26 |
--------------------------------------------------------------------------------
/pages/index/fileUpload.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
31 |
--------------------------------------------------------------------------------
/config/index.js:
--------------------------------------------------------------------------------
1 | // 永中云服务-云预览
2 | // 文档管理接口url
3 | const yzPreviewDmc = 'http://dmc.yozocloud.cn'
4 | const yzPreviewEic = 'http://eic.yozocloud.cn'
5 | const yzPreviewAPPID = 'appId'
6 | const yzPreviewAPPKEY = 'appKey'
7 | /**
8 | * api前缀
9 | */
10 | const apiYzPreviewDmc = '/apiYzPreviewDmc'
11 | const apiYzPreviewEic = '/apiYzPreviewEic'
12 | /**
13 | * 针对不同平台的baseUrl
14 | */
15 | const getYzPreviewDmc = () => {
16 | // #ifdef H5
17 | return apiYzPreviewDmc
18 | // #endif
19 | // #ifndef H5
20 | return yzPreviewDmc
21 | // #endif
22 | }
23 | const getYzPreviewEic = () => {
24 | // #ifdef H5
25 | return apiYzPreviewEic
26 | // #endif
27 | // #ifndef H5
28 | return yzPreviewEic
29 | // #endif
30 | }
31 | export default {
32 | /**
33 | * 针对不同平台的baseUrl
34 | */
35 | yzPreviewDmcUrl: getYzPreviewDmc(),
36 | yzPreviewEic,
37 | yzPreviewEicUrl: getYzPreviewEic(),
38 | yzPreviewAPPID,
39 | yzPreviewAPPKEY
40 | }
41 |
--------------------------------------------------------------------------------
/pages.json:
--------------------------------------------------------------------------------
1 | {
2 | "pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
3 | {
4 | "path": "pages/index/index",
5 | "style": {
6 | "navigationBarTitleText": "上传目录文件解决方案"
7 | }
8 | },
9 | {
10 | "path": "pages/index/androidUpload",
11 | "style": {
12 | "navigationBarTitleText": "Android上传目录文件"
13 | }
14 | },
15 | {
16 | "path": "pages/index/h5Upload",
17 | "style": {
18 | "navigationBarTitleText": "H5上传目录文件(不分系统)"
19 | }
20 | },
21 | {
22 | "path": "pages/index/fileUpload",
23 | "style": {
24 | "navigationBarTitleText": "H5文件上传"
25 | }
26 | },
27 | {
28 | "path": "pages/yzcloud/yz-preview",
29 | "style": {
30 | "navigationBarTitleText": "永中云预览"
31 | }
32 | }
33 | ],
34 | "globalStyle": {
35 | "navigationBarTextStyle": "black",
36 | "navigationBarTitleText": "uni-app",
37 | "navigationBarBackgroundColor": "#F8F8F8",
38 | "backgroundColor": "#F8F8F8"
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/hybrid/html/js/signclient.js:
--------------------------------------------------------------------------------
1 | function generateSign(secret, params) {
2 | var fullParamStr = uniqSortParams(params);
3 | return hmacSHA256(fullParamStr, secret);
4 | }
5 |
6 | function uniqSortParams(params) {
7 | delete params.sign;
8 |
9 | var var5 = [];
10 | var var6 = 0;
11 | for (var key in params) {
12 | var5[var6] = key;
13 | var6++;
14 | }
15 | var5.sort(function (a, b) {
16 | return a.localeCompare(b, 'zh-CN');
17 | });
18 |
19 | var result = "";
20 | for (var var7 = 0; var7 < var5.length; var7++) {
21 | var key = var5[var7]
22 | var var8 = params[key];
23 | var8.sort(function (a, b) {
24 | return a.localeCompare(b, 'zh-CN');
25 | });
26 |
27 | if (var8 != null && var8.length > 0) {
28 | for (var var9 = 0; var9 < var8.length; var9++) {
29 | result += key + "=" + var8[var9];
30 | }
31 | } else {
32 | result += key + "=";
33 | }
34 | }
35 | return result;
36 | }
37 |
38 | function hmacSHA256(data, key) {
39 | data != null ? data : "";
40 | var var2 = CryptoJS.HmacSHA256(data, key);
41 | var var3 = var2.toString(CryptoJS.enc.Hex);
42 | return var3.toUpperCase();
43 | }
44 |
--------------------------------------------------------------------------------
/pages/index/h5Upload.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
22 |
23 |
24 |
25 |
49 |
50 |
63 |
--------------------------------------------------------------------------------
/uni.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * 这里是uni-app内置的常用样式变量
3 | *
4 | * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量
5 | * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App
6 | *
7 | */
8 |
9 | /**
10 | * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能
11 | *
12 | * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
13 | */
14 |
15 | /* 颜色变量 */
16 |
17 | /* 行为相关颜色 */
18 | $uni-color-primary: #007aff;
19 | $uni-color-success: #4cd964;
20 | $uni-color-warning: #f0ad4e;
21 | $uni-color-error: #dd524d;
22 |
23 | /* 文字基本颜色 */
24 | $uni-text-color:#333;//基本色
25 | $uni-text-color-inverse:#fff;//反色
26 | $uni-text-color-grey:#999;//辅助灰色,如加载更多的提示信息
27 | $uni-text-color-placeholder: #808080;
28 | $uni-text-color-disable:#c0c0c0;
29 |
30 | /* 背景颜色 */
31 | $uni-bg-color:#ffffff;
32 | $uni-bg-color-grey:#f8f8f8;
33 | $uni-bg-color-hover:#f1f1f1;//点击状态颜色
34 | $uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色
35 |
36 | /* 边框颜色 */
37 | $uni-border-color:#c8c7cc;
38 |
39 | /* 尺寸变量 */
40 |
41 | /* 文字尺寸 */
42 | $uni-font-size-sm:24upx;
43 | $uni-font-size-base:28upx;
44 | $uni-font-size-lg:32upx;
45 |
46 | /* 图片尺寸 */
47 | $uni-img-size-sm:40upx;
48 | $uni-img-size-base:52upx;
49 | $uni-img-size-lg:80upx;
50 |
51 | /* Border Radius */
52 | $uni-border-radius-sm: 4upx;
53 | $uni-border-radius-base: 6upx;
54 | $uni-border-radius-lg: 12upx;
55 | $uni-border-radius-circle: 50%;
56 |
57 | /* 水平间距 */
58 | $uni-spacing-row-sm: 10px;
59 | $uni-spacing-row-base: 20upx;
60 | $uni-spacing-row-lg: 30upx;
61 |
62 | /* 垂直间距 */
63 | $uni-spacing-col-sm: 8upx;
64 | $uni-spacing-col-base: 16upx;
65 | $uni-spacing-col-lg: 24upx;
66 |
67 | /* 透明度 */
68 | $uni-opacity-disabled: 0.3; // 组件禁用态的透明度
69 |
70 | /* 文章场景相关 */
71 | $uni-color-title: #2C405A; // 文章标题颜色
72 | $uni-font-size-title:40upx;
73 | $uni-color-subtitle: #555555; // 二级标题颜色
74 | $uni-font-size-subtitle:36upx;
75 | $uni-color-paragraph: #3F536E; // 文章段落颜色
76 | $uni-font-size-paragraph:30upx;
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # uniapp-file-upload
2 | uni-app系统目录文件上传(非只图片和视频)解决方案
3 |
4 | ## 永中云服务
5 |
6 | 1.0.1新版本已经发布~
7 |
8 | 新版本基于永中云服务-云预览,完善并实现了跨平台H5文件(非只图片和视频)上传,[永中开发者平台](https://open.yozocloud.cn/)。
9 |
10 | 参考技术文档[《跨平台文档在线预览解决方案(五)-水印、防复制、在线编辑等》](https://juejin.cn/post/6938247508325842958)
11 |
12 | ## 背景
13 | 公司领导提出这样的产品需求:需要上传目录文件,不只是图片和视频,而且同时要支持Android和IOS两大移动端。另外公司App的架构采用的是uni-app。
14 |
15 | ## 思考
16 | * 第一个想到的方案就是,看uni-app框架能否支持。答案可想而知,uni-app组件本身没有提供文件上传组件,不支持``````
17 | * uni-app App端内置HTML5+引擎,提供plus接口,对于Android系统可以直接调用Android系统函数,打开系统目录。而对于IOS而言,没有找到使用方法
18 | * 既然内置HTML5+引擎,能否直接嵌入H5页面呢?当然可以。于是采用H5方式实现
19 |
20 | ## H5页面文件上传
21 | 嵌入H5页面,需要采用web-view标签,如下:
22 |
23 |
24 | 注意:
25 | * h5页面必须在项目目录:/hybrid/html/下面,因为这样uni-app才不会进行编译
26 | * @message事件是h5页面向应用发送数据的回调
27 |
28 | ## web-view传递数据问题
29 | ### 1、@message
30 | 第一种解决方法:通过@message传递数据,在h5页面中,上传完文件后,获取上传后的文件信息,直接postMessage后,web-view页面会接收
31 | ```js
32 | uni.postMessage({
33 | data: {
34 | action: data
35 | }
36 | });
37 | ```
38 | #### 问题
39 | 当运行代码的时候,并没有执行@message回调,需要点击h5页面返回的时候,才会调用回调函数。于是在执行完postMessage后,调用如下函数返回上一级页面
40 | ```js
41 | uni.navigateBack({
42 | delta: 1
43 | });
44 | ```
45 |
46 | 注意:
47 | * 在h5页面中调用uni-app接口时,需要添加uni SDK
48 |
49 |
50 | * 如果要让web-view的上一级页面,即表单页面接收数据,解决方法是:放到store中,表单页面从store中获取
51 |
52 | ### 2、页面跳转url传递数据
53 | 第二种解决方法:通过页面跳转url传递数据。在h5页面上传完文件后,调用页面跳转函数,将文件数据放到url参数中,如下:
54 | ```js
55 | uni.redirectTo({
56 | url: './h5Upload?fileData=' + data,
57 | })
58 | ```
59 |
60 | ## Demo
61 | github:https://github.com/silianpan/uniapp-file-upload
62 |
63 | * 两种方案
64 |
65 |
66 |
67 | * 表单页
68 |
69 |
70 |
71 | * 选择系统目录文件
72 |
73 |
74 |
75 | * 页面跳转url传递数据
76 |
77 |
78 |
79 | ## 附:Android选择系统目录
80 |
81 |
82 | 转载请注明:[溜爸 » uni-app系统目录文件上传(非只图片和视频)解决方案](http://silianpan.cn/index.php/2019/09/22/uniapp_file_upload/)
83 |
--------------------------------------------------------------------------------
/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name" : "uniapp-file-upload",
3 | "appid" : "",
4 | "description" : "",
5 | "versionName" : "1.0.1",
6 | "versionCode" : 101,
7 | "transformPx" : false,
8 | /* 5+App特有相关 */
9 | "app-plus" : {
10 | "usingComponents" : true,
11 | "splashscreen" : {
12 | "alwaysShowBeforeRender" : true,
13 | "waiting" : true,
14 | "autoclose" : true,
15 | "delay" : 0
16 | },
17 | /* 模块配置 */
18 | "modules" : {},
19 | /* 应用发布信息 */
20 | "distribute" : {
21 | /* android打包配置 */
22 | "android" : {
23 | "permissions" : [
24 | "",
25 | "",
26 | "",
27 | "",
28 | "",
29 | "",
30 | "",
31 | "",
32 | "",
33 | "",
34 | "",
35 | "",
36 | "",
37 | "",
38 | "",
39 | "",
40 | "",
41 | "",
42 | "",
43 | "",
44 | "",
45 | ""
46 | ]
47 | },
48 | /* ios打包配置 */
49 | "ios" : {},
50 | /* SDK配置 */
51 | "sdkConfigs" : {}
52 | }
53 | },
54 | /* 快应用特有相关 */
55 | "quickapp" : {},
56 | /* 小程序特有相关 */
57 | "mp-weixin" : {
58 | "appid" : "",
59 | "setting" : {
60 | "urlCheck" : false
61 | },
62 | "usingComponents" : true
63 | },
64 | "mp-alipay" : {
65 | "usingComponents" : true
66 | },
67 | "mp-baidu" : {
68 | "usingComponents" : true
69 | },
70 | "mp-toutiao" : {
71 | "usingComponents" : true
72 | },
73 | "h5" : {
74 | "devServer" : {
75 | "port" : 9091,
76 | "disableHostCheck" : true,
77 | "proxy" : {
78 | "/apiYzPreviewDmc" : {
79 | "target" : "http://dmc.yozocloud.cn",
80 | "changeOrigin" : true,
81 | "secure" : false,
82 | "pathRewrite" : {
83 | "^/apiYzPreviewDmc" : ""
84 | }
85 | },
86 | "/apiYzPreviewEic" : {
87 | "target" : "http://eic.yozocloud.cn",
88 | "changeOrigin" : true,
89 | "secure" : false,
90 | "pathRewrite" : {
91 | "^/apiYzPreviewEic" : ""
92 | }
93 | }
94 | }
95 | },
96 | "title" : "uniapp-file-upload",
97 | "router" : {
98 | "mode" : "hash",
99 | "base" : "/"
100 | }
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/hybrid/html/js/h5uploader.js:
--------------------------------------------------------------------------------
1 | /**!
2 | * h5uploader.js
3 | * This is a simple file upload plugin depends on HTML5.
4 | * You can use it to mordern browser.
5 | *
6 | * version: 1.0
7 | * Copyright 2015, Ziv | http://imziv.com
8 | * Released under the MIT License
9 | * https://github.com/wewoor/h5uploader
10 | **/
11 |
12 | (function(window) {
13 |
14 | window.H5Uploader = (function() {
15 |
16 | function createXhr() {
17 | if (typeof XMLHttpRequest != "undefined") {
18 | return new XMLHttpRequest();
19 | } else if (typeof ActiveXObject != "undefined") {
20 | if (typeof arguments.callee.activeXString != "string") {
21 | var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp"];
22 | for (var i = 0, len = versions.length; i < len; i++) {
23 | try {
24 | new ActiveXObject(versions[i]);
25 | arguments.callee.activeXString = versions[i];
26 | break;
27 | } catch (e) {
28 | throw new Error('Create XHR ActiveXObject error.' + e);
29 | }
30 | }
31 | }
32 | return new ActiveXObject(arguments.callee.activeXString);
33 | } else throw new Error('No XHR object avaliable.' + e);
34 | }
35 |
36 | return {
37 | upload: function(literals) {
38 | if (Object.prototype.toString.call(literals) !== '[object Array]') {
39 | this.handUpload(literals);
40 | } else {
41 | for (var i = 0; i < literals.length; i++) {
42 | this.handUpload(literals[i]);
43 | }
44 | }
45 | },
46 |
47 | handUpload: function(literals) {
48 |
49 | if (literals.action === undefined) {
50 | throw new Error('The upload action address option is undefined.');
51 | }
52 |
53 | var xhr = createXhr();
54 | xhr.open("POST", literals.action, true);
55 | // xhr.setRequestHeader("Content-Type", "multipart/form-data");
56 | // xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
57 | xhr.onreadystatechange = function() {
58 | if (xhr.readyState == 4) {
59 | var body = xhr.responseText;
60 | if (xhr.status >= 200 && xhr.status < 300 ||
61 | xhr.status == 304) {
62 | if (literals.success) {
63 | literals.success(body);
64 | }
65 | } else {
66 | if (literals.fail) {
67 | literals.fail(body);
68 | }
69 | }
70 | }
71 | };
72 |
73 | if (!literals.id) {
74 | throw new Error('The upload id option is undefined.');
75 | }
76 | // var myForm = document.getElementById(literals.formId);
77 | var data = new FormData();
78 | var file = document.getElementById(literals.id);
79 | if (!file) {
80 | throw new Error('The upload file element is undefined::id:' + literals.id);
81 | }
82 | var name = file.getAttribute('name');
83 | if (!name) {
84 | throw new Error('The upload file input name is undefined.');
85 | }
86 |
87 | if (literals.size) { // Check file Size
88 | var evt = this.checkSize(file.files, literals.size.max);
89 | if (evt) {
90 | if (literals.size.valide) literals.size.valide(evt);
91 | throw new Error('The upload file size exceed max value.');
92 | }
93 | }
94 |
95 | if (literals.type) { // Check file type
96 | var evt1 = this.checkType(file.files, literals.type.name);
97 | if (evt1) {
98 | if (literals.type.valide) literals.type.valide(evt1);
99 | throw new Error('The upload file type is error.');
100 | }
101 | }
102 |
103 | if (literals.progress) { // Progress
104 | literals.progress();
105 | }
106 |
107 | for (var i = 0; i < file.files.length; i++) {
108 | data.append(name, file.files[i]);
109 | }
110 |
111 | try {
112 | xhr.send(data);
113 | } catch (e) {
114 | throw new Error(e);
115 | }
116 | },
117 |
118 | // Validate file size
119 | checkSize: function(file, size) {
120 | for (var i = 0; i < file.length; i++) {
121 | if (file[i].size > size * 1024) { // bytes
122 | return file[i];
123 | }
124 | }
125 | },
126 |
127 | // Validate file type
128 | checkType: function(file, type) {
129 | for (var i = 0; i < file.length; i++) {
130 | var arr = file[i].name.split(".");
131 | if (type.indexOf(arr[arr.length - 1]) === -1) {
132 | return file[i];
133 | }
134 | }
135 | }
136 | };
137 | })();
138 |
139 | })(window);
140 |
--------------------------------------------------------------------------------
/hybrid/html/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | H5文档上传
7 |
8 |
9 |
10 | H5文档上传
11 |
12 |
13 |
14 |
15 |
16 |
20 |
21 |
22 |
23 |
24 |
25 |
121 |
122 |
123 |
124 |
--------------------------------------------------------------------------------
/pages/index/androidUpload.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 文件路径为:
7 | {{path}}
8 |
9 |
10 |
11 |
12 |
13 |
206 |
207 |
212 |
--------------------------------------------------------------------------------