解决的问题:

  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. 一致性Hash 分析和实现

    一致性Hash 分析和实现 ---title: 1.一致性Hashdate: 2018-02-05 12:03:22categories:- 一致性Hash--- 一下分析来源于网络总结:算法参照自己 ...

  2. Intelij IDEA创建SpringBoot项目 - 配置文件的解释

    springboot介绍 官网:spring.io Spring Boot is designed to get you up and running as quickly as possible, ...

  3. shiro的web.xml的配置

    <servlet> <servlet-name>springDispatcherServlet</servlet-name> <servlet-class&g ...

  4. Spark学习(二)——RDD的设计与运行原理

    Spark的核心是建立在统一的抽象RDD之上,使得Spark的各个组件可以无缝进行集成,在同一个应用程序中完成大数据计算任务.RDD的设计理念源自AMP实验室发表的论文<Resilient Di ...

  5. TimePicker 时间选择器

    用于选择或输入日期 固定时间点 提供几个固定的时间点供用户选择 使用 el-time-select 标签,分别通过star.end和step指定可选的起始时间.结束时间和步长 <el-time- ...

  6. emqtt emq 的主题访问控制 acl.conf

    访问控制(ACL) EMQ 消息服务器通过 ACL(Access Control List) 实现 MQTT 客户端访问控制. ACL 访问控制规则定义: 允许(Allow)|拒绝(Deny) 谁(W ...

  7. flutter button

    flutter button button类型: RaisedButton : 凸起的按钮,其实就是Android中的Material Design风格的Button ,继承自MaterialButt ...

  8. 阶段3 2.Spring_04.Spring的常用注解_7 改变作用范围以及和生命周期相关的注解

    Scope 改成多例 PreDestory和PostConstruct PreDestory和PostConstruct这两个注解了解即可 增加两个方法,分别用注解 没有执行销毁方法. 如果你一个子类 ...

  9. fiddler抓取app的https的包

    线上问题的排查有时候需要抓包,但是是https协议的,则需要安装证书 在Android 6.0 (API level 23)及以前,APP默认信任系统自带的CA证书以及用于导入的CA证书,Androi ...

  10. 报错:Original error: Could not proxy command to remote server. Original error: Error: read ECONNRESET

    问题:Appium的android真机启动手机时,会遇到以下问题: An unknown server-side error occurred while processing the command ...