一、业务场景

   项目中有个功能模块是商品管理,其中需要使用到图片上传,而且是多文件上传,这是比较常规的操作,就自己来写这个功能。

商品管理这个功能,除去图片操作之外,常规的增、删、改、查很快完成,然后在把图片相关操作添加进来。

二、需求分析

   自己以前开发的项目中,涉及文件上传的功能倒是不少,可是涉及多文件上传的功能还真没有接触过。可是这不影响自己来做

这个功能,正好可以借此机会学习学习如何实现多文件上传的功能。要点有两个,一个是前端代码怎么添加多个文件,一个是后台

如何来接收多个文件。搞清楚这两个问题之后,剩下的事情会简单得多。

三、解决方案

  自己首先完成多文件上传的前端代码,核心代码是创建一个FormData对象,然后获取页面中选择的文件数组之后,

循环遍历添加多个文件,并且多个文件是添加到同一个字段上,代码如下

var formData = new FormData();
//循环获取上传个文件
for (var i = 0, len = $("#upload_file")[0].files.length; i < len; i++) {
    var dataTemp = $("#upload_file")[0].files[i];
    //console.log("dataTemp--->", dataTemp);
    var fileName = dataTemp.name;
    var fivarype = fileName.substring(fileName.lastIndexOf('.')).toLowerCase();
    if(fivarype !== '.jpg' && fivarype !== '.png'){
        return layer.msg('错误提示:只能上传jpg或者png格式的图片!');
    }
    var fileSize = dataTemp.size;
    var limitSize = 2 * 1024 * 1024;
    //console.log("fileSize--->", fileSize);
    if(fileSize > limitSize){
        return layer.msg('错误提示:文件大小不能超过2M!');
    }
    formData.append("files", dataTemp);
}

这样写有一个好处,可以单独判断每一个选中的文件是否符合特定的格式,大小是否符合要求,在前端可以做一个

简单的过滤。将所有的文件对象都添加到files这个字段之上,多单文件上传的时候自己以为只能添加一次,可是做

好这个功能之后,发现它可以添加多次。有的网友是直接添加formData.append("files",$("#upload_file")[0]); 这样也没问题,

只是在选择文件的时候,不会做相关的校验。html代码如下,

<!-- 图片文件选择 -->
<div style="display:none;">
    <input type="file" name="file" id="upload_file" onchange="do_upload()" multiple="multiple" accept=".jpg,.png">
</div>

上传文件时,自己使用的是ajax请求来发送数据,代码如下,

var loadStatus = layer.load(1);
//提交数据
$.ajax({
    url: contextPath + '/goods/upload',
    type: 'post',
    headers:{
        'loginToken': "Bearer " + localStorage.loginToken
    },
    data: formData,
    cache: false,//上传文件无需缓存
    contentType: false,//必须
    processData: false,//用于对data参数进行序列化处理 这里必须false
    success: function (result) {
        /**
         {
             code: -1
             count: null
             data: null
             message: "未获取到上传文件信息"
             status: "失败"
             url: null
         }
         */
        //console.log("result--->", result);
        if (result.code === 0) {
            layer.msg("提示信息:图片上传成功!");
            var tempArr = result.data;
            for(var i = 0, len = tempArr.length; i < len; i++){
                filePathList[filePathList.length] = tempArr[i];
            }
            //console.log("图片上传-filePathList--->", filePathList);
            handlerShowPicture();
        }else {
            layer.msg("错误提示: 图片上传失败");
        }

        $("#upload_file").parent().html('<input type="file" name="file" id="upload_file" onchange="do_upload()" multiple="multiple" accept=".jpg,.png">');
        layer.close(loadStatus);
        // setTimeout(function () {
        //     //延迟1 s 在关闭弹出窗口
        //     layer.closeAll();
        // }, 1000);
    },
    error: function () {
        layer.msg('错误提示:上传图片出现异常,请稍后重试!');
        layer.close(loadStatus);
        // setTimeout(function () {
        //     //延迟1 s 在关闭弹出窗口
        //     layer.closeAll();
        // }, 1000);
        $("#upload_file").parent().html('<input type="file" name="file" id="upload_file" onchange="do_upload()" multiple="multiple" accept=".jpg,.png">');
    },
    timeout: 180000,
    complete: function () {
        //$.messager.progress('close');
    }
});

这种方式简单可用,在写过的很多项目中都是使用这种方式来上传文件。

 

后端代码经过反反复复的摸索之后,操作依赖也并不复杂,一个是接收文件数组的时候,直接在方法的形参中进行接收,代码如下

/**
 * @description: 商品图片上传
 * @date: 2022/1/28 15:31
 * @param : MultipartFile[]
 * @return: ResultVO
 */
//@PostMapping("/upload")
@RequestMapping(value="/upload",method= RequestMethod.POST,consumes="multipart/form-data")
public ResultVO uploadFile(@RequestParam MultipartFile[] files){
    List<String> filePathList = null;
    try{
        if(files == null){
            return ResultVOUtil.error(-1, "文件不存在");
        }

        for(int i = 0, len = files.length; i < len; i++){
            MultipartFile file = files[i];

            long fileSize = file.getSize();

            if(fileSize > giftLimitSize || fileSize == 0){
                log.error("文件大小超出限制:" + fileSize);
                return ResultVOUtil.error(-1, "只能上传大小2M以内的图片");
            }

            String fileName = file.getOriginalFilename();
            if(StringUtils.isBlank(fileName)){
                log.error("文件名称错误:" + fileName);
                return ResultVOUtil.error(-1, "文件名称错误");
            }

            String fileSuffixName = fileName.substring(fileName.lastIndexOf(".") + 1);
            if(!typeSet.contains(fileSuffixName)){
                log.error("文件后缀名错误:" + fileSuffixName);
                return ResultVOUtil.error(-1, "文件后缀名错误");
            }
        }

        filePathList = this.goodsManageService.uploadPicture(files);

    }catch (Exception ex){
        log.error("图片上传操作异常--->" + ex.getMessage());
        ex.printStackTrace();
    }

    log.info("图片上传返回数据为--->" + filePathList);
    if(Objects.equal(filePathList, null)){
        return ResultVOUtil.error(-1, "图片上传操作失败");
    }else{
        return ResultVOUtil.success("上传成功", filePathList);
    }
}

在service中具体的处理逻辑如下,
@Override
public List<String> uploadPicture(MultipartFile[] files) throws Exception {
    if(files == null){
        log.error("参数-files-错误{}:", files);
        return null;
    }

    String targetPath = String.join(File.separator, linuxPath, "3");
    File tempPath = new File(targetPath);
    if(!tempPath.exists()){
        tempPath.mkdirs();
    }

    String destFileName = "";
    List<String> filePathList = new ArrayList<String>();
    try{
        for(int i = 0, len = files.length; i < len; i++) {
            MultipartFile file = files[i];
            String fileName = file.getOriginalFilename();

            String fileSuffixName = fileName.substring(fileName.lastIndexOf(".") + 1);

            destFileName = CommonUtil.getUUID() + "." + fileSuffixName;
            String targetFile = String.join(File.separator, targetPath, destFileName);
            File destFile = new File(targetFile);
            if(!destFile.exists()){
                destFile.createNewFile();
            }
            file.transferTo(destFile);

            String returnPath = File.separator + String.join(File.separator, "3", destFileName);
            boolean isWin = System.getProperty("os.name").toLowerCase().contains("win");
            if(isWin){
                //windows 系统 进行路径替换 将 / 替换为 \\
                returnPath = returnPath.replaceAll("\\\\", "/");
            }

            filePathList.add(returnPath);
        }
    }catch (Exception ex){
        log.error("文件保存异常:" + ex.getMessage());
        return null;
    }


    return filePathList;
}

主要做的一个操作就是接收文件数组,然后将文件保存到自己指定的一个目录下面即可,核心代码是

file.transferTo(destFile);file表示文件数组中的对象,destFile表示目标文件的全路径,包含文件后缀名,
这句代码的含义就是将上传的文件保存到指定的文件当中。最后按照自己业务的要求返回指定的文件路径,不是文件的
全路径,因为在显示图片的时候传的参数是一个相对路径,后台处理的时候加上路径前缀即可。图片显示的方法如下,
public void showPicture(String filePath, HttpServletResponse response) throws Exception {
    if(StringUtils.isBlank(filePath)){
        log.error("参数-filePath-错误");
        return;
    }
    String realPath = linuxPath + filePath;
    String result = "-1";
    File file = new File(realPath);
    response.setContentType("image/jpeg");

    try (FileInputStream fis = new FileInputStream(realPath);
         OutputStream os = response.getOutputStream();){
        int len = 0;
        byte[] buffer = new byte[1024];
        while ((len = fis.read(buffer)) != -1){
            os.write(buffer, 0, len);
        }
        os.flush();
    } catch (Exception ex) {
        log.error("图片显示服务错误:" + ex.getMessage());
    }
}
至此整个多文件上传的功能,全部搞定,功能完成,效果如下

iOS atnewtk 多文件上传 多文件上传前端_多文件上传