图片上传使用的是easyUI的组件,并不是自己实现的.
实现图片上传的前提是配置了文件上传的解析器(applicationContext-mvc.xml):
1.1 前端的代码:
kingEditorParams : {
filePostName : "uploadFile",
uploadJson : '/pic/upload',
dir : "image"
},
这是前端common.js实现的封装的代码,当一点击开始上传的时候,就是访问的这个路径,会把图片发送给服务端(下面这张图是使用了Nginx反向代理之后实现的图片回显的效果)
当一点击开始上传之后,会把图片通过post请求发送给服务端,处理请求的路径是/pic/upload
,文件上传的名字是uploadFile
1.2 服务端的实现:
FileController:具体的业务放在service层
@Controller
public class FileController {
@Autowired
private FileUploadService fileUploadService;
@RequestMapping("/pic/upload")
@ResponseBody
public PicUploadResult uploadPic(MultipartFile uploadFile) {
return fileUploadService.uploadPic(uploadFile);
}
}
controller层接受请求的参数名字是
uploadFile
(这个是固定的,前端传过来的),参数的类型是MultipartFile
,MultipartFile
是一个接口,具体的类型是org.springframework.web.multipart.commons.CommonsMultipartFile
,配置的文件上传解析器会将客户端传过来的文件解析成CommonsMultipartFile
.
FileUploadService:
@Service
public class FileUploadServiceImpl implements FileUploadService {
/**
* Spring容器如何动态的获取数据
* @Value : 实现数据的动态获取
* 说明:@value 注解可以直接为String或者基本类型赋值
* 同时,可以利用spring的机制可以为array , list , mapp , set properties 赋值
*
* 注意事项:
* 1.在SSM框架中,该注解需要依赖@Service注解(Spring容器内部才能使用) 在Controller注解可能不好使
* SpringMVC容器 跟 Spring容器 是两个不同的容器
* 2.如果使用SpringBoot则任何地方都能用
*/
@Value("${image.localDir}")
private String localDir;
@Value("${image.urlPre}")
private String urlPre;
@Override
public PicUploadResult uploadPic(MultipartFile uploadFile) { // 图片上传
PicUploadResult result = new PicUploadResult();
// 1.判断是否为图片 abc.jpg
String fileName = uploadFile.getOriginalFilename();
// 使用正则表达式进行判断
if (!fileName.matches("^.*(jpg|png|gif)$")) { // . 代表任意的字符 * 代表有任意多个 的
// 表示不是图片
result.setError(1);
return result;
}
try {
/**
* ImageIO是javax下面的一个工具类
*/
// 2.判断是否为恶意程序 uploadFile.getInputStream() 文件流
BufferedImage bufferedImage = ImageIO.read(uploadFile.getInputStream());
// 3.获取图片宽度和高度 (图片宽高为 0 ,则图片是假的图片)
int height = bufferedImage.getHeight();
int width = bufferedImage.getWidth();
if (height == 0 || width == 0) {
result.setError(1); // 假图片
return result;
}
// 4.准备文件存储的路径()
// String localDir = "d:/file/"; // 本地文件夹
// 5.实现分文件存储 yyyy/MM/dd 以 天 为单位 分文件
String dateDir = new SimpleDateFormat("yyyy/MM/dd").format(new Date());
// 6.形成保存图片的文件夹 d:/file/yyyy/MM/dd (文件存储的根目录)
String picDir = localDir + dateDir;
// 7.生成文件夹
File picFile = new File(picDir); // 文件夹的文件执行
if (!picFile.exists()) { // 文件夹不存在 就创建文件夹
picFile.mkdirs();
}
// 8.重命名上传的文件名字 (避免重名)
String uuid = UUID.randomUUID().toString().replace("-", ""); // asa22exac-adsfafadsf-3fddsfds
// 生成随机数
int randomNum = new Random().nextInt(999); // 生成随机数 (0-999)
// 获取文件的后缀名
String fileType = fileName.substring(fileName.lastIndexOf(".")); // .jpg
// 拼文件名
String realFileName = uuid + randomNum + fileType; // 真实的文件名称
// 9生成文件的本地磁盘的路径 d:/file/yyyy/MM/dd/wqrwadasfiuoew800.jpg
String localPath = picDir + "/" + realFileName;
// 10实现文件的上传
uploadFile.transferTo(new File(localPath));
// 11添加文件的宽度,高度 需要返回
result.setHeight(height + "");
result.setWidth(width + "");
// 12准备文件的虚拟路径
// http://image.jt.com/file/2018/05/07/radweedwsaf210.jpg
// String urlPre = "http://image.jt.com/";
String urlPath = urlPre + dateDir + "/" + realFileName;
result.setUrl(urlPath);
} catch (IOException e) { // 不是图片会抛异常
e.printStackTrace();
result.setError(1); // 表示为恶意程序
return result;
}
return result;
}
}
分析:客户端上传文件,需要先判断上传的文件是不是图片,先使用正则表达式进行第一层过滤,如果正则的验证过了之后,但是可能是伪图片,所以还得使用ImageIO来将上传的文件进行转换成BufferedImage,如果转换失败,那么可能不是图片,转换成功之后,还得获取图片的宽,高,如果宽高任何一个为0,就说明转换之后不是图片,也就说明上传的不是图片.
如果这些验证都过了,说明上传的文件是图片,那么我们就需要将图片保存起来.由于上传的图片名字是固定的,而且保存的时候如果使用原始的图片名字的话,很可能
会出现名字冲突的问题,为了尽可能地避免重现保存图片出现冲突的问题,就需要服务端生产随机的名字,这里采用一种更安全的做法,先使用UUID生成一串随机数,
但是这种方式生成的随机数是根据时间戳来生成的(同一毫秒值生成的uuid随机数可能会一样),所以还得使用Randoom再生成随机的数字,将uuid生成的随机串
(去掉'-')加上生成的随机数字组成图片的名字,这种情况出现重名的概率比较小
注意拿到了图片文件之后,还需要将图片文件保存,所有的图片都保存在一个大的文件夹下(d:/file),但是这个文件夹路径是不能写死的,得用配置文件进行配置,还有
一点就是图片的回显需要一个虚拟的路径.图片上传之后,
图片的上传之后的回显是由easyUI来实现的,但是要求是从服务端返回个数据的页面的json数据是有要求的,
{"error":0,"url":"图片的保存路径","width":图片的宽度,"height":图片的高度}
参数说明: 0代表是一张图片,如果是0,前台才可以解析并显示。1代表不是图片,
不显示如果不设置宽度和高度,则默认用图片原来的大小,所以不用设置
返回的就是 PicUploadResult
public class PicUploadResult {
private Integer error=0; //图片上传错误不能抛出,抛出就无法进行jsp页面回调,所以设置这个标识,0表示无异常,1代表异常
private String url;
private String width;
private String height;
~~~ 此处省略get,set方法
这个返回的对象里面的url需要在服务端进行封装,封装的就是easyUI实现回显图片的虚拟路径,这个url是虚拟路径:
理解这个url(虚拟路径) : 图片有专门的图片服务器,图片的存储也是在专门的服务器里面,异常easyUI要进行图片的回显,就需要访问图片服务器.目前我们这个
项目保存图片是保存在d:file里面,目前的水平只能做到保存在本机(还做不到保存在别的服务器),但是做图片回显的时候,我们就希望模拟实现那种跨服务器的
图片访问(现在用的是同一台主机),所以返回的url是一个虚拟路径,http://image.jt.com/.这就相当于当图片回显时,访问的是这个域名下的图片.
为了实现程序的可扩展性,可以把两个变量写在配置文件里面,一个是localDir
(图片保存的本地根目录),还有一个是urlPre
(图片回显时需要访问的网址)
有了这个properties配置文件之后,好需要加载这个配置文件(在applicationContext里面进行加载)
classpath:/properties/jdbc.properties
classpath:/properties/image.properties
这样在service层就可以直接使用@Value()注解进行动态地赋值注入了