前言

我做的毕设里面的用户页面需要上传头像,现在把详细的业务流程和实现写在这里,供大家参考一下。


有一些细节的前端问题自己解决叭,不然太多了。

一.业务流程

  1. 上传文件并校验文件格式,文件大小,如果都通过则预览文件。
  2. 点击上传之后,更新前端头像,后端保存图片并删除原图片。

二.实现步骤

1.前端内容

先展示一下我的前端页面。
因为我使用jQuery写的js,页面用了bootstrap,也用了它的弹窗插件bootbox,模板引擎用的是thymeleaf,是使用axios做异步的…

1.1 文件校验并预览

设置的全局变量。

	var file; // 定义一个全局变量,为一个文本选择器。
	var chooseImage; // 用于上传的文件
	file = $('<input type="file" />'); // 这样file就是jquery创建的一个文本选择器,但是因为我们并没有把它加载到页面上,所以是不可见的。
	// button的单击事件 
	$('#chooseImg').click(function(){
   
		// 启动文件选择
		file.click();
	});

	// 选择好文件后,获取选择的内容
	file.change(function(e){
   
		var select_file = file[0].files[0];
		
		// 校验文件名称
		var file_path = file.val();
		var extStart = file_path.lastIndexOf("."); // 按.分隔文件名称
		var ext = file_path.substring(extStart, file_path.length).toUpperCase(); // 取出后缀并转为大写
		if (ext != ".BMP" && ext != ".PNG" && ext != ".JPG" && ext != ".JPEG") {
   
			bootbox.alert({
   
			    size: "small",
			    title: "提示",
			    message: "图片仅限于bmp,png,jpg,jpeg的格式!",
				buttons: {
   
					ok: {
   
						label: '确认',
						className: 'btn-primary'
					}
				},
			    callback: function(){
    /* your callback code */ }
			})
			return false;
		}
		
		// 校验文件大小
		if (select_file.size > 1048576) {
   
			bootbox.alert({
   
			    size: "small",
			    title: "提示",
			    message: "图片大小不能超过1M!!",
				buttons: {
   
					ok: {
   
						label: '确认',
						className: 'btn-primary'
					}
				},
			    callback: function(){
    /* your callback code */ }
			})
			return false;
		}
		
		chooseImage = select_file; // 赋值给全局变量 --> 用来做上传的操作

				// 展示到页面
                 var reader = new FileReader();// 读取文件URL
                 reader.readAsDataURL(chooseImage);
                 reader.onload = function() {
   
                     // 读取的URL结果:this.result
                     $("#re_headImg").attr("src", this.result).show();
                 }

		$("#uploadImg").removeClass("update-head-msg-bt-dis");
		$("#uploadImg").removeAttr("disabled");
		$("#uploadImg").attr("title","选择提交即可上传该图片文件作为头像.");
	});
});

这段代码总体就是三步走,因为我是代码中生成input,它的type为file,所以页面是无法显示,用一个全局变量来接收,做上传使用 -> chooseImage (就是它),第一步就是校验它的文件格式,用substring分割字符换,第二步就是校验文件大小,第三步就是回显到页面,具体的业务根据自己的来设计。

得到的效果如下所示。

1.2 文件的提交并上传

我在上面对提交按钮设置了disabled,在选择完文件后removeAttr删除它的属性,接下来对form表单进行异步提交, 设置了 event.preventDefault(); 使事件无效,单纯用来异步。

// 头像上传提交事件
 $("#uploadHeadImg_form").submit(function () {
   
			event.preventDefault();
			var formData = new FormData(); // 要提交的内容封装进formdata
			formData.append("file", chooseImage);
			formData.append('userId', [[${
   userInfo.id}]])
			axios({
   
			method: 'post',
			url: '/uploadImage/uploadHeadImg',
			data: formData
			})
         .then(function (response) {
   
             if (response.data.state == 0) {
    // 说明上传失败
                 bootbox.alert({
   
                     size: "small",
                     title: "修改提示",
                     message: "上传头像失败",
                     buttons: {
   
                         ok: {
   
                             label: '确认',
                             className: 'btn-primary'
                         }
                     },
                     callback: function(){
    /* your callback code */ }
                 })
             } else if (response.data.state == 1) {
    // 说明添加成功

                 // 更新页面数据
                 var reader = new FileReader();// 读取文件URL
                 reader.readAsDataURL(chooseImage);
                 reader.onload = function() {
   
                     // 读取的URL结果:this.result
                     $("#avatar_img").attr("src", this.result).show(); // 异步更新页面上的头像图片
                     $("#local_headImg").attr("src", this.result).show(); // 异步更新页面上的头像图片
                     $("#uploadImg").addClass("update-head-msg-bt-dis");
                     $("#uploadImg").attr("disabled","disabled");
                     $("#uploadImg").attr("title","请先选择要上传的图片文件.");
                 }

                 bootbox.alert({
   
                     size: "small",
                     title: "修改提示",
                     message: "上传头像成功",
                     buttons: {
   
                         ok: {
   
                             label: '确认',
                             className: 'btn-primary'
                         }
                     },
                     callback: function(){
    /* your callback code */ }
                 })
             }

         })
         .catch(function (error) {
   
             console.log(error);
         });
 });

这段代码就是做异步提交工作,如果成功则更改页面属性,失败则弹窗报错。

2.后端内容

2.1 依赖文件

		<!--thymeleaf模板引擎-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--fileupload-->
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3</version>
        </dependency>

2.1配置文件

controller接收参数,文件和要更改头像用户的id,使用MultipartFile接收文件。逻辑比较简单,大家看一下就行了。
我将文件路径都写在全局配置文件application.properties

#文件上传地址
file.upload.path.realPath=D://idea_springBoot_place/second-hand-website/src/main/resources/static/upload/
file.upload.path.relativePath=/upload/**

在通过@configurationProperties来通过配置文件来解耦。

@Component
@ConfigurationProperties(prefix = "file.upload.path")
public class MyUploadPathProperties {
   

    /** * 文件保存路径 */
    private String realPath;

    /** * 文件保存相对路径 */
    private String relativePath;

    public String getRealPath() {
   
        return realPath;
    }

    public void setRealPath(String realPath) {
   
        this.realPath = realPath;
    }

    public String getRelativePath() {
   
        return relativePath;
    }

    public void setRelativePath(String relativePath) {
   
        this.relativePath = relativePath;
    }
}

这里是实现文件上传和删除的主要逻辑代码。
这是controller的逻辑,就是通过File类来保存和删除。

@Controller
@RequestMapping("/uploadImage")
public class UploadImageController {
   

    @Autowired
    MyUploadPathProperties myUploadPathProperties;

    @Autowired
    UserService userService;

    @ResponseBody
    @PostMapping("/uploadHeadImg")
    public Object uploadHeadImg (MultipartFile file, Integer userId) {
   
        String oldName = file.getOriginalFilename(); // 获取上传文件名
        // path = D://idea_springBoot_place/second-hand-website/src/main/resources/static/upload/headImg/
        String path = myUploadPathProperties.getRealPath() + "headImg/"; // 定义上传文件路径
        String fileName = changeName(oldName); // 改名
        String rappendix = "/upload/headImg/" + fileName; // 文件相对路径
        // File.separator +
        fileName = path + fileName; // 文件绝对路径

        File file1 = new File(fileName); // 新建文件
        if (!file1.exists()) {
   
            //先得到文件的上级目录,并创建上级目录,在创建文件
            file1.getParentFile().mkdir();
            try {
   
                //创建文件
                file1.createNewFile();
            } catch (IOException e) {
   
                throw new UpLoadException("创建文件失败!");
            }
        }
        // 写入文件
        try {
   
            file.transferTo(file1);
        } catch (IOException e) {
   
            throw new UpLoadException("写入文件失败!");
        }
        System.out.println(rappendix);
        System.out.println(file1);

        //-------------------------------
        // 删除原文件
        String oldPath = userService.findById(userId).getHeadImg();
        String oldFileName = myUploadPathProperties.getRealPath() + oldPath.substring(8); // 从 /upload/ 后面开始拼接
        if (!oldPath.equals("/upload/headImg/default.jpg")) {
    // 默认头像不删除
            if (!delete(oldFileName)) {
   
                throw new UpLoadException("删除原文件失败");
            }
        }


        // ------------------------------
        // 操作数据库修改
        userService.updateHeadImg(userId, rappendix);

        return new JsonResult(rappendix);

    }

    /** * 改名 * @param oldName * @return */
    public static String changeName(String oldName){
   
        Random r = new Random();
        Date d = new Date();
        String newName = oldName.substring(oldName.indexOf('.'));
        newName = r.nextInt(99999999) + d.getTime() + newName;
        System.out.println(newName);
        return newName;

    }

    /** * 删除文件 * @param filePath * @return */
    public static Boolean delete(String filePath) {
   
        File file = new File(filePath);
        if (file.exists()) {
   
            file.delete();
            System.out.println("===========删除成功=================");
            return true;
        } else {
   
            System.out.println("===============删除失败==============");
            return false;
        }
    }

}

在service层就是修改user表中的img路径。

得到效果如下所示

结语

如果有什么问题请给我留言吧,一起互相进步互相学习。