业务需求:将zip格式的文件导入到系统,解析其中的所有图片,要求必须是图片还要限制文件大小不大于200Kb、不能出现中文,符合要求的图片上传云端或者本地保存-返回保存地址,然后再根据图片文件命名的idcard来绑定人员图片信息写入数据库。

单个文件的情况下,我们可以直接使用multipartFile对象的方法,直接获取文件名和大小等信息。但是现在传过来的file是压缩包,要自己解压再去读取文件名,直接百度好吧。

================没有s图================

查到的结果有一大半都是另存到磁盘,然后解压到当前目录,这样确实可以,但老是感觉这样不好(编程新手的傻傻分不清),可能是因为一直在io太麻烦不想搞。然后我就继续找,发现了apache自带的一个输入流 ZipImputStream 。

GBK

getNextEntry() 方法,返回的 ZipEntry

@ApiOperation(value = "批量采集",notes = "导入zip图片文件夹压缩包")
    @PostMapping("/batchAdd")
    @Log(title = "人脸管理【批量采集照片】",businessType = BusinessType.IMPORT)
    public AjaxResult batchAdd(MultipartFile file) throws Exception{//tomcat默认最大大小为20mb
        if (ObjectUtil.isNull(file)){
            return AjaxResult.error("上传文件为空");
        }
        String fileName = file.getOriginalFilename();
        System.out.println("文件名:"+fileName);
        if (!"zip".equals(fileName.substring(fileName.lastIndexOf(".")+1))){
            return AjaxResult.error(300,"请上传zip格式的压缩包");
        }
        if (file == null || file.getSize() <= 0){
            return AjaxResult.error(300,"上传压缩文件为空");
        }
        //解析zip文件
        ZipInputStream zipInputStream = null;
        ZipEntry zipEntry = null;
        int num = 0;
        try {
            zipInputStream = new ZipInputStream(file.getInputStream(), Charset.forName("GBK"));

            while ((zipEntry = zipInputStream.getNextEntry())  != null){
                String name = zipEntry.getName();
                System.out.println("文件夹名:"+name);
                if (name.endsWith("/") || name.endsWith("\\")){
                    System.out.println("存在空文件夹,文件夹名为:"+zipEntry.getName());
                    continue;
                }
                num++;
                //空校验
                String[] split ;
                String photoName = null;
                if (name.contains("/")){
                    split = name.split("/");
                    photoName = split[split.length-1];
                }else if (name.contains("\\")){
                    split = name.split("\\\\");
                    photoName = split[split.length-1];
                }else {
                    photoName = name;
                }
                String idCard = photoName.substring(0,photoName.indexOf("."));
                System.out.println(num+" 文件名:"+photoName);
                if (StringUtils.isEmpty(photoName)){
                    throw new RuntimeException("获取文件名错误");
                }
                //图片类型校验
                String suffix = photoName.substring(photoName.lastIndexOf(".") + 1);
                if (!("jpg".equalsIgnoreCase(suffix) || "png".equalsIgnoreCase(suffix))){
                    throw new RuntimeException("压缩包存在不符合图片格式(jpg和png)的文件");
                }
                //校验图片大小 大于20kb,小于200kb
                if (zipEntry.getSize() < new Long(20*1024) || zipEntry.getSize() > new Long(200*1024)){
                    throw new RuntimeException("存在图片不符合要求,请检查小于20KB和大于200KB的图片并予以修改");
                }
                //校验图片命名不存在中文
                Pattern compile = Pattern.compile("[\u4e00-\u9fa5]");
                Matcher matcher = compile.matcher(photoName);
                if (matcher.find()){
                    throw new RuntimeException("图片命名存在中文");
                }
                //调用百度接口,识别人脸  读取照片文件-调用上传-添加采集记录
                ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                byte[] bytes = new byte[4096];
                int len = 0;
                while ((len = zipInputStream.read(bytes)) > 0){
                    outputStream.write(bytes,0,len);
                }
                outputStream.flush();
                outputStream.close();

                ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
                MultipartFile multipartFile = getMultipartFile(inputStream, photoName);

                System.out.println("idcard:"+idCard);
                AjaxResult ajaxResult = photoUploadAndCollect(multipartFile,idCard);
                //如果上传出错,抛出异常信息激活事务
//                System.out.println("处理图片返回结果:"+ajaxResult);
                System.out.println("code:"+ajaxResult.get("code"));
                System.out.println(!"200".equals(String.valueOf(ajaxResult.get("code"))));
                if (!"200".equals(String.valueOf(ajaxResult.get("code")))){
                    throw new RuntimeException(String.valueOf(ajaxResult.get("msg")));
                }
                zipInputStream.closeEntry();
                inputStream.close();
            }
            zipInputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (zipInputStream != null){
                zipInputStream.close();
            }
        }
        return AjaxResult.success("批量采集图片成功,共上传 "+num+" 张图片");
    }

         ZipEntry获得到的文件名格式如下:

/新建文件夹/435765199712305649.jpg

/新建文件夹/新建文件夹/

/新建文件夹(1)/435765199712305649.jpg

说明:如上所示,他会依次一直读到文件为止。当最后一级为文件夹时,就是第二个种情况,表示这是一个空文件夹。

上传图片:

当信息校验完成之后,就可以先上传照片了,可以利用已有的上传接口,但是参数是 multipartFile

类型,就又百度了一个字节流封装成MyltipartFile的方法(粘过了稍微改造了一下完美),如下所示:

(118条消息) 图片上传文件流格式转换 File文件流转为MultipartFile流详解_文件流转multipartfile_guobinhui的博客

/**
     * 获取封装得MultipartFile
     *
     * @param inputStream inputStream
     * @param fileName    fileName
     * @return MultipartFile
     */
    public MultipartFile getMultipartFile(InputStream inputStream, String fileName) {
        FileItem fileItem = createFileItem(inputStream, fileName);
        //CommonsMultipartFile是feign对multipartFile的封装,但是要FileItem类对象
        return new CommonsMultipartFile(fileItem);
    }


    /**
     * FileItem类对象创建
     *
     * @param inputStream inputStream
     * @param fileName    fileName
     * @return FileItem
     */
    public FileItem createFileItem(InputStream inputStream, String fileName) {
        FileItemFactory factory = new DiskFileItemFactory(16, null);
        String textFieldName = "file";
        FileItem item = factory.createItem(textFieldName, MediaType.MULTIPART_FORM_DATA_VALUE, true, fileName);
        int bytesRead = 0;
        byte[] buffer = new byte[10 * 1024 * 1024];
        OutputStream os = null;
        //使用输出流输出输入流的字节
        try {
            os = item.getOutputStream();
            while ((bytesRead = inputStream.read(buffer, 0, 8192)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            inputStream.close();
        } catch (IOException e) {
//            log.error("Stream copy exception", e);
            throw new IllegalArgumentException("文件上传失败");
        } finally {
            if (os != null) {
                try {
                    os.close();
                } catch (IOException e) {
//                    log.error("Stream close exception", e);

                }
            }
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
//                    log.error("Stream close exception", e);
                }
            }
        }

        return item;
    }

 至此,我已经保存好了图片,只需要将图片和人员关联信息保存到数据库就圆满结束了。我已经有了图片地址,根据图片命名的idcard查一下人员信息,得到数据保存到数据库。如下所示:

private AjaxResult photoUploadAndCollect(MultipartFile multipartFile, String idCard) throws Exception{
        //1查询人员信息
        PersonnelInfo pi = personnelInfoService.getPersonnelInfoDetailByIdCard(idCard);
        if (ObjectUtil.isNull(pi)){
            throw new RuntimeException("获取身份证号为:"+idCard+"  的人员信息失败");
        }
        QueryWrapper<PersonnelBiologyInfo> biologyInfoQueryWrapper = new QueryWrapper<>();
        biologyInfoQueryWrapper.eq("enable_flag",0);
        biologyInfoQueryWrapper.eq("personnel_info_id",pi.getId());
        PersonnelBiologyInfo biology = personnelBiologyInfoService.getOne(biologyInfoQueryWrapper);
        if (ObjectUtil.isNull(biology)){
            biology = new PersonnelBiologyInfo();
            biology.setCreateTime(new Date());
            biology.setCreateUser(Integer.valueOf(String.valueOf(getUserId())));
        }else {
            biology.setUpdateTime(new Date());
            biology.setUpdateUser(Integer.valueOf(String.valueOf(getUserId())));
        }
        biology.setBiologyType("1");
        biology.setPersonnelInfoId(pi.getId());
        biology.setAuditStatus("0");
        biology.setEnableFlag(0);
        biology.setSource(0);
        //2图片上传,接口有调用百度接口-识别人脸返回评分
        AjaxResult ajaxResult = collectPictures(multipartFile);
        if (!"200".equals(String.valueOf(ajaxResult.get("code")))){
            return ajaxResult;
        }
        String fileName = String.valueOf(ajaxResult.get("fileName"));
        //3添加采集记录
        biology.setBiologyInfo(fileName);
        biology.setFaceQuality(String.valueOf(ajaxResult.get("pf")));
        boolean b = personnelBiologyInfoService.saveOrUpdate(biology);
        if (!b){
            throw new RuntimeException("身份证号为:"+idCard+" 保存人员生物信息失败");
        }
        return AjaxResult.success();
    }

微调一下,完美结束,运行正常。

一个简单的小功能,记录一下,有问题还望指正,大佬勿喷心灵比较弱小。

大概就是使用apache自带的zipinputstream获取zip压缩包的数据流,然后通过网上搜的方法将每一个图片流转换成mulitpartFile类型,直接调用了若依已有方法上传保存,结束。先放这里,回头仔细说明一下。