说明:
(1)本篇博客的内容:富文本编辑器summernote简介;发文章,之开发【多文件上传,接口】;
目录
二:summernote中是可以贴多张图片的,这就涉及到了多文件上传;
(1)在【api】接口工程的FileUploaderControllerApi接口中,定义多文件上传接口;
(2)在【files】文件服务的FileUploaderController类中,去实现多文件上传接口;
三:【多文件上传,接口】需要用户登录、并且用户是已经激活的状态下,才能调用的;
(1)以前在【SSM开发书评网28:后台一:富文本编辑器wangEditor使用入门;】中,自己就使用过百度的wangEditor;
(2)其实,在GitHub中也有很多很多其他开源的富文本编辑器,都可以使用;
(3)我们这儿使用的是summernote这款;
(1)summernote中,可以上传一张图片,也可以上传多张图片;
(2)如果是上传一张图片,其实在【44:第四章:开发文件服务:5:在【files】文件服务中,整合FastDFS,实现【上传头像】的逻辑;】中上传用户头像时,就是上传一张图片,即单文件上传;
(3.1)而这儿的多文件上传,就需要重新开发一个接口;
(3.2)其实,当我们向summernote中放入多张图片时,前端会进行一个构建,构建成一个list数组;然后,发送给后端;后端,在进行相应的处理;
(4)声明:"summernote中贴的图片"的上传会使用【多文件上传,接口】,"文章封面图片"的上传使用的也是【多文件上传,接口】;
文件上传这个功能,虽然在业务上属于【article】文章服务;但是,就功能上来说,我们把其放在了【files】文件服务中;
(1)在【api】接口工程的FileUploaderControllerApi接口中,定义多文件上传接口;
/** * 【多文件文件,接口】 * @param userId:用户id; * @param files:前端传过来的文件; * @return * @throws Exception :因为这个接口是可能发生异常的,所以我们这儿先throws一下(打个提前亮);(然后,我们 * 在【24:第三章:开发通行证服务:7:自定义异常(来表征程序中出现的错误);创建GraceExceptionHandler来全 * 局统一处理异常(根据异常信息,构建对应的API统一返回对象的,JSON数据);】已经编写了应对这种情况情况的逻辑, * 如果忘记了,可以快速去参考) */ @PostMapping("/uploadSomeFiles") //设置路由,这个是需要前后端约定好的; public GraceJSONResult uploadSomeFiles(@RequestParam("userId") String userId, MultipartFile[] files) throws Exception;说明:
(1)这个接口的url、请求方式、参数不是瞎写的,需要前后端一致;
(2)前端传送多文件的时候,后端这儿直接使用【MultipartFile[]】去承接即可,即这儿多了一个[];
● 自然,后端接口这儿的文件名,需要和前端保持一致;
(2)在【files】文件服务的FileUploaderController类中,去实现多文件上传接口;
/** * 【多文件文件,接口】 * @param userId:用户id; * @param files:前端传过来的文件; * @return * @throws Exception :因为这个接口是可能发生异常的,所以我们这儿先throws一下(打个提前亮);(然后,我们 * 在【24:第三章:开发通行证服务:7:自定义异常(来表征程序中出现的错误);创建GraceExceptionHandler来全 * 局统一处理异常(根据异常信息,构建对应的API统一返回对象的,JSON数据);】已经编写了应对这种情况情况的逻辑, * 如果忘记了,可以快速去参考) */ @Override public GraceJSONResult uploadSomeFiles(String userId, MultipartFile[] files) throws Exception { // 1. 声明一个List:(1)我们把图片上传到阿里云OSS或者FastDFS后,会返回一个该图片的url;(2)然后,后端会 // 把这个地址返回给前端,以便在前端显示;(3)因为我们这儿可能是上传多张图片,所以声明了一个List; ListimageUrlList = new ArrayList<>(); // 2. 前端传过来的files不能为空,同时里面也必须得有内容; if (files != null && files.length > 0) { // 3. 循环遍历files; for (MultipartFile file : files) { //定义一个path变量,让其承接该次循环,上传的文件的url; String path = ""; //4.判断前端传过来的file是否为空; // 4.1 如果文件不为空,我们就去处理; if (file != null) { //5. 获得文件的原始名称; String fileName = file.getOriginalFilename(); //6. 使用【org.apache.commons.lang3】提供的工具,去判断一下文件名是否为空; // 7.1 如果文件名不为空,我们就去处理; if (StringUtils.isNotBlank(fileName)) { //通过截取最后一个“.”后面的内容,获取文件扩展名 //String suffix = fileName.substring(fileName.lastIndexOf(".")); //8. 这儿采用了另一个逻辑,来获取文件扩展名:先利用"."把文件名拆成了一个数组;然后,获取最后一个元素,就是扩展名; String[] fileNameArr = fileName.split("\\."); String suffix = fileNameArr[fileNameArr.length - 1]; //8. 这儿出于安全考虑:这个接口有可能被黑客攻击;黑客可能上传一些.jsp、.php、.sh脚本文件等;如果黑客把这些文件上 // 传了,并且运行;那么,其就可以直接攻击我们的服务器了;所以,我们要判断一下其上传的文件的后缀,是否符合我们的规 // 范;比如,这儿我们就可以规定图片文件只能是.jpg、.png、.jpeg格式的 //8.1 如果文件格式不符合要求; if (!suffix.equalsIgnoreCase("png") && !suffix.equalsIgnoreCase("jpg") && !suffix.equalsIgnoreCase("jpeg")) { //如果文件图片格式既不是"png"、也不是"jpg"、还不是"jpeg"的话;就返回返回提示"文件图片格式不支持!"的GraceJSONResult; // return GraceJSONResult.errorCustom(ResponseStatusEnum.FILE_FORMATTER_FAILD); //注:由于我们这儿是多文件上传,如果在上传中间某个文件出错了,就抛出的话;用户体验是不好的;所以,我们 // 这儿的做法时,如果中间上传某个文件出错时,我们就continue跳过此次循环,直接上传下一个文件; continue; } else {//8.2 如果能执行到这儿,说明文件时OK的(自然,文件格式也是符合要求);调用service层的方法,去上传文件; // path = uploaderService.uploadFdfs(file, suffix);//调用Fdfs的逻辑 path = uploaderService.uploadOSS(file, userId, suffix);//调用阿里OSS的逻辑 } } else { //7.2 如果文件名为空,直接返回提示"文件不能为空,请选择一个文件再上传!"的GraceJSONResult; // return GraceJSONResult.errorCustom(ResponseStatusEnum.FILE_UPLOAD_NULL_ERROR); //注:由于我们这儿是多文件上传,如果在上传中间某个文件出错了,就抛出的话;用户体验是不好的;所以,我们 // 这儿的做法时,如果中间上传某个文件出错时,我们就continue跳过此次循环,直接上传下一个文件; continue; } } else { //4.2 如果文件为空,直接返回提示"文件不能为空,请选择一个文件再上传!"的GraceJSONResult; // return GraceJSONResult.errorCustom(ResponseStatusEnum.FILE_UPLOAD_NULL_ERROR); //注:由于我们这儿是多文件上传,如果在上传中间某个文件出错了,就抛出的话;用户体验是不好的;所以,我们 // 这儿的做法时,如果中间上传某个文件出错时,我们就continue跳过此次循环,直接上传下一个文件; continue; } //如果能执行到这儿,说明上面的一切顺利; logger.info("path= " + path);//把返回的文件路径信息,打印一下日志, //最后,还要判断一下,其返回的path是否为空 String finalPath = ""; if (StringUtils.isNotBlank(path)) { //如果文件名没问题,就拼接文件的最终全路径 // finalPath = fileResource.getHost() + path;//调用Fdfs的storage服务的IP地址和端口号,拼接最终的全路径 finalPath = fileResource.getOssHost() + path;//调用阿里OSS的【https】+【Bucket 名称】+【Endpoint】,拼接最终的全路径 //将此次循环上传的文件,上传后的路径存到imageURLList中去; // FIXME:其实,在放入imageURLList之前,需要对图片进行一下审核;(只是,图片审核功能,自己并没有做) imageUrlList.add(finalPath); } else { //如果返回的path为空,就返回一个返回提示"文件上传失败!"的GraceJSONResult; // return GraceJSONResult.errorCustom(ResponseStatusEnum.FILE_UPLOAD_FAILD); //注:由于我们这儿是多文件上传,如果在上传中间某个文件出错了,就抛出的话;用户体验是不好的;所以,我们 // 这儿的做法时,如果中间上传某个文件出错时,我们就continue跳过此次循环,直接上传下一个文件; continue; } } } return GraceJSONResult.ok(imageUrlList); }说明:
(1)这儿其实就是在 【44:第四章:开发文件服务:5:在【files】文件服务中,整合FastDFS,实现【上传头像】的逻辑;】的基础上,做了一层循环处理;看注释;
(2)在上传多个文件时,如果上传某个文件出错时,直接continue跳过此次循环即可;其实,在接口处理完返回给前端的时候,前端会做一次判断,如果多张图片中有几张图片上传失败的话,其会给出提示;
(1)先install一下整个项目;(2)记得使用SwitchHost开启虚拟域名映射;(3)使用Tomcat启动前端项目;(4)然后,启动后端项目;
利用我们在【36:第三章:开发通行证服务:19:拦截那些“想要访问【获得用户账户信息,接口】和【更改/完善用户信息,接口】的请求“,检查登录信息,校验通过后再放行;(使用【Spring MVC中的拦截器】来实现)】中创建的UserTokenInterceptor拦截器;在InterceptorConfig类中配置UserTokenInterceptor拦截器的地方,增加对【多文件上传,接口】的拦截;
利用我们在【37:第三章:开发通行证服务:20:拦截那些“想要访问【需要用户是激活状态,才能访问的接口】的请求“,去检查用户状态;(使用【Spring MVC中的拦截器】来实现)】中创建的UserActiveInterceptor拦截器;在InterceptorConfig类中配置UserActiveInterceptor拦截器的地方,增加对【多文件上传,接口】的拦截;