0%

ssp前端

ssp frontend

file-upload前端原理

1. file-upload.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<div>
<iic-panel-top title="上传文件" chain="文件管理"></iic-panel-top>

<div class="row">
<div class="col-12">
<div class="card">

<div class="card-body">
<div id="upload-select-area" class="upload-select-area">
<h1 @click="onSelected()">拖拽文件到这里</h1>
</div>
<div class="upload-btn-area" v-show="showBtnArea()">
<button class="btn btn-sm btn-primary waves-effect waves-light" @click="onUploadAll()">全部上传</button>
<button class="btn btn-sm btn-secondary waves-effect waves-light" @click="onDelAll()">全部清除</button>
</div>
<div class="upload-preview-area">
<ul>
<li v-for="fileinfo in fileDatas">
<div class="preview-item">
<div class="preview-img"><img :src="fileinfo.url"></div>
<div class="preview-cont">
<div>
<label>名称</label>
<span>{{fileinfo.name}}</span>
</div>
<div><label>大小</label><span>{{fileinfo.size}}</span></div>
<div><label>状态</label>
<span v-if="fileinfo.status==1" class="text-primary" style="font-weight: 600;"><b class="mdi mdi-checkbox-marked-circle"></b>已上传</span>
<span v-if="fileinfo.status==0" class="text-warning" style="font-weight: 600;"><b class="mdi mdi-information"></b>未上传</span>
</div>
<div style="margin-top:15px;"><label>操作</label><span><a href="javascript:void(0)" class="btn btn-sm btn-primary" @click="onUpload(fileinfo)" :class="showUploadBtn(fileinfo)">上传</a> <a href="javascript:void(0)" class="btn btn-sm btn-danger" @click="onDelete(fileinfo)">删除</a></span></div>
</div>
<div class="preview-loading" v-show="showLoading(fileinfo)"></div>
</div>
</li>
</ul>
</div>
</div>
</div>
</div>
</div> <!-- end row -->
</div>

重点在于:

  1. ```html
  2. 1
    2
    3
    4
    5
    6
    7

    负责展示用户上传的文件经整理后的fileinfo



    2. ```html
    <a href="javascript:void(0)" class="btn btn-sm btn-primary" @click="onUpload(fileinfo)" :class="showUploadBtn(fileinfo)">上传</a>

  1. ```html
    <button class=”btn btn-sm btn-primary waves-effect waves-light” @click=”onUploadAll()”>全部上传
    1
    2
    3
    4
    5
    6
    7
    8
    9



    用户拖拽一个文件进入上传框后,逻辑如下:

    0. 一进入该html页面,执行该Vue对象的 init 方法,oUpload 对象初始化一系列只与当前用户有关、与所选择上传文件无关的属性

    1. ```js
    var dObj = new Dropzone("#upload-select-area", option);

​ 通过Dropzone,绑定对象 option 和 id=”upload-select-area” 的 div 组件,一旦拖拽/选择文件,便执行 option 中的 addedfile: function(file) 方法

  1. addedfile 方法传入用户选择上传的文件 file,进行类型判断,并增加了一些属性,和 file 本身,一起赋给Vue对象中 data 里的fileDatas: []

​ 若用户选择多个文件,则fileDatas数组中有多个经处理后规范的fileinfo文件对象

  1. 规范的fileinfo对象被页面展示

    注意:通过Vue,并非直接操纵DOM树,而是先js操纵Vue组件,再由Vue实现对DOM树的操纵、即页面的展示的变化

2. file-upload.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
define(function(require, exports, module) {
require('iic-panel-top');

require("../../assets/libs/dropzone/dropzone.css");
require("../../assets/libs/dropzone/dropzone.js");

var css = require("./file-upload.css");
var tpl = require("./file-upload.html");

module.exports = Vue.extend({
data: function data() {
return {
fileDatas: [], //{fileId:'', name:'文件名', size:'12345676', url:'', enc:0, status:0未上传1已上传, file:文件}
uploadUrl: '/mgrapi/admin/file/upload',

maxSize: '2M',
suffix: ['.jpg','.jpeg','.png','.gif','.bmp'],

oUpload: null

};
},

mounted: function(){
this.initUpload();
},

template: tpl,

methods:{
initUpload: function(){
var _this = this;

this.oUpload = IICUpload.create({
token: "123456",
fileElId: '', //file组件的ID
uploadUrl: _this.uploadUrl, //文件上传的后台处理路径
infoUrl: '', //读取文件信息路径
params: { //文件上传的其他参数
uploadType: 1, //上传类型(0:上传一般文件,1:上传临时文件,2:加密上传文件, 3:指定路径上传文件)
newName: '', //新文件名
oldName: '', //旧文件名
savePath: '' //保存的目录路径
},
suffix: _this.suffix, //后缀
maxsize: _this.maxSize, //文件大小
succFn: showSuccAlert, //成功的回调方法
failFn: showErrAlert //失败的回调方法
});

var option = {
url: '',
addedfile: function(file){
var _size = _this._getFileSize(file);
var filename = file.name;

if(!_this._checkFileSize(file)) return;
//判断文件类型
var _type = $FileUtil.getFileType(filename);
if(_type=="Other") return;

if(_type == 'Image'){
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function(e){
_this.fileDatas.push({
fileId: '',
name: filename,
size: _size,
url: e.target.result,
enc: 0,
status: 0,
file: file
});
};
return;
}

var _srcUrl = '/assets/images/fileicon/file.png';

if(_type == 'PDF') _srcUrl='/assets/images/fileicon/pdf.png';
if(_type == 'Word') _srcUrl='/assets/images/fileicon/word.png';
if(_type == 'Excel') _srcUrl='/assets/images/fileicon/excel.png';
if(_type == 'Video') _srcUrl='/assets/images/fileicon/video.png';
if(_type == 'Zip') _srcUrl='/assets/images/fileicon/zip.png';

_this.fileDatas.push({
fileId: '',
name: filename,
size: _size,
url: _srcUrl,
enc: 0,
status: 0,
file: file
});
}
};
var dObj = new Dropzone("#upload-select-area", option);
},

onUploadAll: function(){
var _this = this;
var cycleFn = function(){
var _fileinfo = _this._getNeedFileinfo();
if(_fileinfo){
_this.onUpload(_fileinfo, cycleFn);
}
};
cycleFn();
},

onUpload: function(fileinfo, succFn, failFn){
var _this = this;
succFn = succFn || showSuccAlert;
failFn = failFn || showErrAlert;

this.oUpload.option.params.oldName = fileinfo.name;
//如果指定了路径就是上传指定路径文件
if (fileinfo.savePath){
this.oUpload.option.params.savePath = fileinfo.savePath;
this.oUpload.option.params.uploadType = 1;
} else {
this.oUpload.option.params.uploadType = 0;
}
fileinfo.loading = 1;
_this.$forceUpdate();

var file = fileinfo.file;
this.oUpload.uploadFile(file, function(info){
fileinfo.fileId = info.fileId;
fileinfo.status = 1;
fileinfo.loading = 0;
_this.$forceUpdate();

succFn("上传成功");
}, function(msg){
fileinfo.loading = 0;
_this.$forceUpdate();
failFn(msg);
});
},

onDelAll: function(){
this.fileDatas = [];
},

onDelete: function(fileinfo){
var index = -1;
$.each(this.fileDatas, function(i,obj){
if(obj==fileinfo){
index = i;
}
});
if(index>=0){
this.$delete(this.fileDatas, index);
}
},

showUploadBtn: function(fileinfo){
if(fileinfo.status==1) return 'disabled';
return '';
},

showBtnArea: function(){
if(this.fileDatas.length==0) return false;
return true;
},

showLoading: function(fileinfo){
return fileinfo.loading;
},

onSelected: function(){
$('#upload-select-area').click();
},

_getNeedFileinfo: function(){
for(var i=0,len=this.fileDatas.length;i<len;i++){
var fileinfo = this.fileDatas[i];
if(fileinfo.status == 0) return fileinfo;
}
return null;
},

_getFileSize: function(file){
var _size = parseInt(file.size);

if(_size<1024) return _size;

if(_size<1024*1024){
_size = _size/1024;
_size = _size.toFixed(1)+'KB'
return _size;
}

_size = _size/(1024*1024);
_size = _size.toFixed(1)+'M'

return _size;
},

_checkFileSize: function (file){
var _size=parseInt(file.size);
if(_size<1024*1024*2) return true;
return false;
}
}
});
});

点击上传后,文件传到后端Controller的部分逻辑如下:

  1. 向onUpload中传入fileinfo

  2. onUpload 调用 this.oUpload.uploadFile方法,传入 var file = fileinfo.file; 以及成功与失败的回调函数

  3. 通过this获取到option中的token和回调函数

  4. 判断token后通过AJAX POST发送fileData到后端Controller,其中fileData中包含fileinfo中的file属性,token属性以及所有其他属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
IICUpload.prototype.uploadFile = function(file, succFn, failFn){
var _this = this;
this.option.succFn = succFn || this.option.succFn;
this.option.failFn = failFn || this.option.failFn;

//判断token
if(!this.option.token){
_this.option.failFn('token不能为空');
return;
}

if(!file){
_this.option.failFn('文件不能为空');
return;
}

var fileData = new FormData();
fileData.append("file", file);
fileData.append("token", this.option.token);
//加入其他参数
fileData = this.__addParams(fileData);

//Ajax提交上传
var url = this.option.uploadUrl;
$.ajax({
type: "POST",
url: url,
contentType: false,
processData: false,
data: fileData,
dataType: "json",
success: function(res) {
var success = res.success;
if(success){
var fileinfo = res.data;
_this.fileInfo = fileinfo;
_this.option.succFn(fileinfo);
}else{
var msg = res.msg;
_this.option.failFn(msg);
}
}
});
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function IICUpload(option){
this.option = {
token: '', //上传权限令牌,必填
fileElId: '', //file组件的ID
uploadUrl: '', //文件上传的后台处理路径
infoUrl: '', //读取文件信息路径
params: { //文件上传的其他参数
uploadType: '', //上传类型(1:上传一般文件, 2:指定路径上传文件, 3:加密上传文件, 4:上传临时文件)
newName: '', //新文件名
oldName: '', //旧文件名
savePath: '' //保存的目录路径
},
suffix: [], //后缀
maxsize: '2M', //文件大小
succFn: null, //成功的回调方法
failFn: null //失败的回调方法
};

this.fileInfo = {}; //文件信息

this.option = IICUpload.__initOption(option);
}

ajax Post发送后,通过注解@RequestParam(value = “file”),Spring MVC将前端的”file”属性自动匹配地赋给对象MultipartFile file,前端”file”中多余属性则忽视,缺失属性则置为null; 其余属性则自动匹配地赋给对象ULFileInfoVo fileInfo,用于后端的后续业务逻辑。

Spring MVC:

  1. 可以在SpringMVC的Controller中具体请求处理方法中,直接使用基本数据类型或者String类型的参数进行对映,只要前端页面和方法中指定的参数名称是完全一致的,这里包括大小写,那么前端这个参数名称所赋有的值,就会传递到后台的请求处理方法中。
    由SpringMVC底层的拦截器实现

    它会将请求中传来的所有参数,请求处理方法中的参数进行对比,如果发现是一模一样的,它就会从请求中把那个参数的值拿出来赋给处理方法中的那个变量,以供后端controller继续操作。

  2. SpringMVC的过滤器不光接收映射同名的参数,而且还会将类型帮助我们转换成功。但是,类型的转换,存在着问题,比如:int类型是整数类型,刚才传来的参数id就是int类型,而值恰好是1,则不会出现问题,可是如果将值改成abc等非数字的内容呢?那肯定是会报错的,因为数字格式异常,无法进行转换。同时,还有传递的时候忘记传递id属性而只传了name属性,或者id属性只给了key,而没有value值,这些情况,都会导致错误的出现。

  3. 本例中,后端JavaBean来接收参数 并加入@RequestParam(value = “file”)注解

    @RequestParam 注解用来标注在控制器方法的参数上,springmvc 从 request 中获取请求的值赋值给方法的参数 @RequestParam 指定 name 时,可以获取 request 中指定参数的值,相当于 request.getParameter (name)或 request.getParameters (name)

  4. restful风格传值方式:

    使用RESTful风格开发Java Web - 简书 (jianshu.com)

    REST是REpresentational State Transfer的缩写(一般中文翻译为表述性状态转移),REST 是一种体系结构,而 HTTP 是一种包含了 REST 架构属性的协议,为了便于理解,我们把它的首字母拆分成不同的几个部分:

    • 表述性(REpresentational): REST 资源实际上可以用各种形式来进行表述,包括 XML、JSON 甚至 HTML——最适合资源使用者的任意形式;
    • 状态(State): 当使用 REST 的时候,我们更关注资源的状态而不是对资源采取的行为;
    • 转义(Transfer): REST 涉及到转移资源数据,它以某种表述性形式从一个应用转移到另一个应用。

    简单地说,REST 就是将资源的状态以适合客户端或服务端的形式从服务端转移到客户端(或者反过来)。在 REST 中,资源通过 URL 进行识别和定位,然后通过**行为(即 HTTP 方法)**来定义 REST 来完成怎样的功能。

  5. springMVC接收JSON参数详解_白面小生的博客-CSDN博客_springmvc如何接收json数据

    GET请求想必大家都不陌生,它将参数以url?username=”admin”&password=123这种方式发送到服务器,并且request.getParameter可以接收到这种参数,我们在浏览器地址栏上也可以看到这一点。而我们Ajax使用的POST,并且发送的是JSON对象,那么后台是如何获取到的呢?答案就在于这个Content-Type x-www-form-urlencoded的编码方式把JSON数据转换成一个字串,(username=”admin”&password=123)然后把这个字串添加到url后面,用?分割,(是不是和GET方法很像),提交方式为POST时候,浏览器把数据封装到HTTP BODY中,然后发送到服务器。所以并不会显示在URL上。

    注意:
    (1)当Ajax以application/x-www-form-urlencoded格式上传即使用JSON对象,后台需要使用@RequestParam 或者Servlet获取。
    (2) 当Ajax以application/json格式上传即使用JSON字符串,后台需要使用@RquestBody获取。

  6. 在 ajax 中 contentType 设置为 false 是为了避免 JQuery 对其操作,从而失去分界符,而使服务器不能正常解析文件。

  7. ajax中processData: (默认: true) 默认情况下,发送的数据将被转换为对象(技术上讲并非字符串) 以配合默认内容类型 “application/x-www-form-urlencoded”。如果要发送 DOM 树信息或其它不希望转换的信息,请设置为 false。

3. 后端Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
@RestController
@RequestMapping(path = "/mgrapi/admin/file")
public class AdminFileController extends MgrBaseController {

private static final Log log = LogFactory.getLog(AdminFileController.class);

@Autowired
private IAdminFileService iAdminFileService;


@ApiOperation(value = "上传图片", httpMethod = "POST")
@RequestMapping(path = "/upload", method = RequestMethod.POST)
public JsonResult uploadFile(@RequestParam(value = "file") MultipartFile file, ULFileInfoVo fileInfo) {
try{
LllFileV2 fileinfo = iAdminFileService.uploadFile(file,fileInfo);
return JsonResult.createSuccess(fileinfo, "");
}catch (Exception ex){
log.error("",ex);
return JsonResult.createFail(ex);
}
}

@GetMapping(path = "/token")
public JsonResult fetchToken(){
try{
String token = iAdminFileService.fetchToken();
return JsonResult.createSuccess(token, "");
}catch (Exception ex){
return JsonResult.createFail(ex);
}
}

@ApiOperation(value = "查询文件列表", notes = "查询文件列表", httpMethod = "POST")
@RequestMapping(path = "/query")
public JsonResult queryFileList(@RequestParam int pageno, @RequestParam int pagesize, String id) {
try {
SysUser sysUser = this.getCurrentSysUser();
ListPageVo<AdminFileVo> list = iAdminFileService.queryFilePage(pageno, pagesize, id, sysUser);

return JsonResult.createSuccess(list, "OK");
} catch (Exception ex) {
log.error(ex);
return JsonResult.createFail(ex);
}
}
}

杂记

SSP: self-service platform
IDR: 标识解析展现 ID Read
API:

FileCenter

工业互联网标识:国家制定的规范 统一的公共数据格式

88.168.100/xxxxxxxx
标识前缀:中国.鑫兴.
顶级节点.二级节点.企业节点

https://ssp.gdsinsing.com/#/statis/home/user-home

https://ssp.gdsinsing.com/index.html#/statis/home/user-home

https默认端口443

http默认端口80

nginx 443

ssp 8095

#/statis/home/user-home=>#/statis/home/user-home.js

require 异步加载

https://ssp.gdsinsing.com/statis/home/user-home.js

https://ssp.gdsinsing.com/statis/home/user-home.html

web页面 一般都是短连接

postman
form-data :上传文件
x-www-form-urlencoded :普通post 键值对,表单数据
raw :数据写在body内 非键值对 RESTful 手机 流行 整个json传过去

authId对应应用白名单,并不对应每个用户

isCont为false则不读文件内容,只读文件路径等信息