解决的问题:

  1、使用view的<Upload>组件实现图片文件的上传。

  2、<Upload>组件action请求地址无法到自己写的后台。

  3、前台base64的图片展示。

  4、文件伪造。(修改文件的后缀为图片格式的后缀)。

需求很简单:

  将某一模块的编辑页面的"XX图片"字段由文本框输入改成上传图片。或者新增一个图片上传的属性。

  要求:

  1、 前端图片展示用BASE64格式的形式展示。

一、完成后的效果图:

1、上传前

2、点击相机,选择图片文件进行上传。

2.1 文件格式的校验

2.2 文件大小的限制

3、上传后

4、点击图片中的眼睛图标,可以对图片进行放大查看。

5、点击图片中的垃圾筒的图标,可以对图片进行删除。回到上传前。

二、前端vue组件代码。

官网地址:

https://www.iviewui.com/components/upload

<template>
<div class="demo-upload-list" v-if="formData.stationPic">
<template>
<img :src="formData.stationPic">
<div class="demo-upload-list-cover">
<Icon type="ios-eye-outline" @click.native="handleView()"></Icon>
<Icon type="ios-trash-outline" @click.native="handleRemove()"></Icon>
</div>
</template>
</div>
<Upload
ref="upload"
:show-upload-list="false"
:on-success="handleSuccess"
:format="['jpg','jpeg']"
:max-size="2048"
:on-format-error="handleFormatError"
:on-exceeded-size="handleMaxSize"
:before-upload="handleBeforeUpload"
:headers="headers"
type="drag"
action="/api/zclanes/upload"
style="display: inline-block;width:58px;">
<div style="width: 58px;height:58px;line-height: 58px;">
<Icon type="ios-camera" size="20"></Icon>
</div>
</Upload>
<Modal title="View Image" v-model="visible">
<img :src="formData.stationPic" v-if="visible" style="width: 100%">
</Modal>
</template>
    data() {
return {
headers: {'Sonep-Token': CacheUtil.getSession('access-token')},
visible: false,
},
    methods: {
handleView() {
this.visible = true;
},
handleRemove() {
this.formData.stationPic = null;
},
handleSuccess(res, file) {
if (res.status === 200) {//上传成功
this.$Message.success('上传成功');
this.formData.stationPic = res.data;
} else {
this.$Message.error('上传失败');
}
},
handleFormatError(file) {
this.$Notice.warning({
title: '文件格式不正确',
desc: file.name + '的文件格式不正确, 请选择jpg或者jpeg格式的图片'
});
},
handleMaxSize(file) {
this.$Notice.warning({
title: '超出文件大小限制',
desc: file.name + '文件太大,不能超过2M.'
});
},
handleBeforeUpload() {//上传文件之前的钩子,参数为上传的文件,若返回 false 或者 Promise 则停止上传 } },

三、后端代码

3.1 controller

    /**
* 图片上传
* @param file
* @return
*/
@RequestMapping("/upload")
public ResponsePayload upload(MultipartFile file){ return service.upload(file);
}

3.2 service

    @Override
public ResponsePayload upload(MultipartFile file) { try {
//判断文件是不是图片
BufferedImage image = ImageIO.read(file.getInputStream());
if (image == null || image.getWidth() <= 0 || image.getHeight() <= 0) {
return ResponseUtil.getFailResponse(HttpStatus.SC_BAD_REQUEST, "你上传的不是图片文件");
}
} catch (IOException e) {
e.printStackTrace();
return ResponseUtil.getFailResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "上传异常, 请稍后再试...");
} //获取存放文件的临时路径
String filePath = IOHelper.checkPath(System.getProperty("java.io.tmpdir") + "img/");
// String filePath = FileUtils.getDataFilePath("d:/img");
//1. 获取文件的原始名称
String originalFilename = file.getOriginalFilename();//timg (1).jpg
//1.1 获取最后一个.的位置
int lastIndexOf = file.getOriginalFilename().lastIndexOf(".");
//1.2 获取文件的后缀名 .jpg
String suffix = originalFilename.substring(lastIndexOf);
//2. 重命名文件名称
String fileName = UUID.randomUUID().toString() + suffix;
File savedFile = new File(filePath, fileName);
try {
file.transferTo(savedFile);
//转BASE64
String path = savedFile.getPath();
//返回BASE64图片字符串
return ResponseUtil.success(ImageBase64Utils.toBase64(path));
} catch (IOException e) {
e.printStackTrace();
return ResponseUtil.getFailResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR,"上传异常, 请稍后再试...");
} finally {
//删除本地保存的图片
savedFile.delete();
} }

四、问题回顾及解决

我们依次看下文章开头提到的4个问题

4.1 使用view的<Upload>组件实现图片文件的上传。

可以看下本文的第二部分:前端vue组件代码。

同时也给出官网地址:

https://www.iviewui.com/components/upload

4.2 <Upload>组件action请求地址无法到自己写的后台。

这个问题很恶心。

4.2.1 action地址的写法

官网用的是自己写的请求地址:action="//jsonplaceholder.typicode.com/posts/"。仔细一看是什么鬼,怎么有两个斜杠。

运行下官网的例子:

我们看到发送的请求是:https://jsonplaceholder.typicode.com/posts/。

仔细分析下:发现地址前面是带域名的:jsonplaceholder.typicode.com

这里有几种写法:

写法一:如果域名是不变的。

action="//localhost:8080/api/zclanes/upload"  ---> http://localhost:8080/api/zclanes/upload

写法二:不加域名的写法。(本文用的是这种)

action="/api/zclanes/upload"   ---> http://localhost:8080/api/zclanes/upload

4.2.2 请求的发送

填写完正确的地址后,后台代码也写好了。重启项目后,请求发送不出去。

我这里最后查出来是少加了个请求头。猜测:项目有auth功能,每个请求都必须加鉴权的请求头进行鉴权。如果有跟我一样的问题,找到要加的请求头,把请求头加上去就ok了。部分代码如下,详细代码上文有。其实想说明的是,<Upload>组件请求头如何添加。 其实还可以添加请求参数,具体参考官网API。

:headers="headers"
headers: {'Sonep-Token': CacheUtil.getSession('access-token')},

4.2.3 前台base64的图片展示。

参考地址:https://www.cnblogs.com/hjw-zq/p/8821898.html

其实就一句代码:

formData.stationPic: 就是后台返回的BASE64的字符串。
<img :src="formData.stationPic">

base图片展示原理: https://www.cnblogs.com/zdz8207/p/web-image-base64.html

个人理解:将图片文件转成BASE64格式的字符串。由页面自动会解析BASE64格式的图片文件。跟图片文件所在路径没有关系。也就是说,转完BASE64格式后,你把本地的图片删了都没事。

4.2.4 文件伪造。(修改文件的后缀为图片格式的后缀)。

其实解决完上面三个问题,这个功能完成的就八九不离十了。 当我在测文件大小限制的时候,我电脑里没找到2M的图片,于是我就找了个2M的excel文件,把它的后缀名改成了图片格式进行上传。测试通过。于是我就想到了,如果小于2M的文件改了会上传成功么。 答案肯定是不允许的,因为它本质就不是图片。测试也能上传成功,但就是显示不出图案来。这肯定是不对的。

于是我就想到了还少个判断文件是否是图片的逻辑。

Java判断文件是否是图片:
参考地址:https://blog.csdn.net/itjauser/article/details/97395034

五、小结:

图片上传看似简单的一个动作,其实过程很复杂。

流程大致如下:

1、选择上传的文件,发送请求。(这里请求的业务逻辑有很多种解决方案,可以把图片上传到服务器(七牛云、阿里云、本地的数据库等)。或者转成BASE64格式的字符串。不管哪种实现方式,目的都是为了把图片以某种形式存放起来,并将图片地址存放的地址返回,在前台进行显示)。

2、 将图片以BASE64的形式存储。

3、 返回BASE64字符串。

4、 拿到后台响应的图片地址,进行展示。

ImageBase64Utils 工具类
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.misc.BASE64Encoder; import java.io.File;
import java.io.FileInputStream;
import java.io.IOException; public class ImageBase64Utils {
private static Logger log = LoggerFactory.getLogger(ImageBase64Utils.class);
public static String toBase64(String filePath) {
FileInputStream fis = null;
String base64Prefix = "data:image/jpeg;base64,";
String imgStr = "";
try {
File file = new File(filePath);
if(file.exists()) {
fis = new FileInputStream(file);
byte[] buffer = new byte[(int) file.length()];
int offset = 0;
int numRead = 0;
while (offset < buffer.length && (numRead = fis.read(buffer, offset, buffer.length - offset)) >= 0) {
offset += numRead;
}
if (offset != buffer.length) {
log.error("图片文件未读取完毕!");
}
fis.close();
BASE64Encoder encoder = new BASE64Encoder();
imgStr = base64Prefix + encoder.encode(buffer);
}
} catch (Exception e) { } finally {
if(fis != null) {
try {
fis.close();
} catch (IOException e) {
}
}
}
return imgStr;
}
}

最后,不喜勿喷。 有想法可以在下面留言,一起交流。

ivew组件上传图片文件的功能:的更多相关文章

  1. angularJs中上传图片/文件功能:ng-file-upload

    原文技术交流:http://www.ncloud.hk/%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB/angularjs-ng-file-upload/ 在做网站的过程中难 ...

  2. .NET平台开源项目速览(13)机器学习组件Accord.NET框架功能介绍

    Accord.NET Framework是在AForge.NET项目的基础上封装和进一步开发而来.因为AForge.NET更注重与一些底层和广度,而Accord.NET Framework更注重与机器 ...

  3. .NET跨平台之旅:增加文件日志功能遇到的挫折

    在将我们的ASP.NET 5示例站点(about.cnblogs.com)升级至ASP.NET 5 RC1的时候,我们增加了控制台日志功能. 在ASP.NET 5添加日志功能很简单,只需在projec ...

  4. 利用其它带文件防护功能的软件防止*.asp;*.jpg写入文件。

    此木马是一个.NET程序制作,如果你的服务器支持.NET那就要注意了,,进入木马有个功能叫:IIS Spy,点击以后可以看到所有站点所在的物理路径.以前有很多人提出过,但一直没有人给解决的答案.. 防 ...

  5. 转: KindEditor 图片空间文件增加删除文件、文件夹功能(ASP语言环境)

    KindEditor 图片上传功能中集成的图片空间文件管理插件可以对已上传图片进行管理,十分便捷,只是没有图片删除功能,仔细研读xieliang分享的经验后,自己动手改造了一下,顺便分享给有同样需求的 ...

  6. 利用 FormData 对象和 Spring MVC 配合可以实现Ajax文件上载功能

    Ajax文件上载 利用 FormData 对象和 Spring MVC 配合可以实现Ajax文件上载功能: 步骤 导入组件并准备静态脚本 <dependency> <groupId& ...

  7. C#使用FileSystemWatcher控件实现的文件监控功能示例

    本文实例讲述了C#使用FileSystemWatcher控件实现的文件监控功能.分享给大家供大家参考,具体如下: FileSystemWatcher 可以使用FileSystemWatcher组件监视 ...

  8. JS 上传图片 + 预览功能(一)

    JS 上传图片 + 预览功能 <body> <input type="file" id="fileimg1" style="disp ...

  9. jquery组件WebUploader文件上传用法详解

    这篇文章主要为大家详细介绍了jquery组件WebUploader文件上传用法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 WebUploader是由Baidu WebFE(FEX)团队开发的一 ...

随机推荐

  1. Android学习_7/24

    一.           活动的生命周期 1.        活动状态 运行:位于返回栈栈顶 暂停:不在栈顶,但仍可见 停止:不在栈顶,完全不可见 销毁:从栈中移除 2.        活动的生存期 ...

  2. Java第09次实验(IO流)--实验报告

    0.字节流与二进制文件 我的代码 用DataOutputStream和FileOutputStream将Student对象写入二进制文件student.data package test; impor ...

  3. 排序算法总结(java实现)

    排序算法 介绍:排序分为内部排序和外部排序,内部排序指在内存中进行的排序过程:外部排序指在外存中进行排序的过程,但是此过程建立在多次访问内存的基础上(分成一段段利用内部排序进行排序). 以下排序均属于 ...

  4. JAVA-Runnable、Callable、FutureTask

    通常,创建线程的执行单元有两种,一种是直接继承 Thread,另外一种就是实现 Runnable 接口. 但这两种都有一个问题就是无法有返回值,且子线程在执行过程中无法抛出异常.想线程有返回值,可以使 ...

  5. 关系代数(Relation Algebra)与SQL语句的对应关系

    SQL语句的执行一般是先翻译为关系代数再被执行的(能有效提高执行速度),所以我们有必要 了解关系代数与SQL语句间的对应关系. 就像高中代数由+-*/和数字组成,关系代数是由union.interse ...

  6. 编辑器UEditor入门学习

    优点:非常使用的富文本编辑器,对比于之前使用的summernote,比前者多出了更多的字体图标 废话少说,直接步骤: 1.导入资源(全部放在单独的文件下即可,下图为“UEditor”文件夹) 2.引用 ...

  7. 进程管理工具之supervisor[安装并使用]

    在Linux服务器中,有时候我们需要一个进程需要可靠的在后台运行,并且能够监控进程状态,在意外结束时能够自动重启等.此时就可以使用supervisor. supervisor 是使用Python开发的 ...

  8. java: (正则表达式,XML文档,DOM和DOM4J解析方法)

    常见的XML解析技术: 1.DOM(基于XML树结构,比较耗资源,适用于多次访问XML): 2.SAX(基于事件,消耗资源小,适用于数量较大的XML): 3.JDOM(比DOM更快,JDOM仅使用具体 ...

  9. 使用vue做项目如何提高代码效率

    最近做了两个vue项目,算上之前做的两个项目,总共有四个vue项目的经验了,但是总体来说写的代码质量不是很高,体现在以下几点 1.代码没有高效的复用 自从使用vue做项目之后,以前使用面向过程变成的习 ...

  10. MYSQL5.5 linux安装

    1.常规的编译安装MYSQL 此种方法使用所有Mysql5.0 - 5.1 系列产品 比较常规的编译方式 2. 采用cmake 方式编译安装Mysql 3.二进制安装方式 免编译安装MYSQL 4.如 ...