出现该异常是在调用 file.getBytes()时,网上也有在调用file.getInputStream()时也会出现该异常。经阅读相关文章资料,该异常会在多线程时偶尔出现,单线程不会出现,原因与上传文件大小有关,有的提出修改文件上传相关参数大小,以springboot项目为例:

application.properties配置文件中添加配置:

spring.http.multipart.maxFileSize=10Mb    // 设置单个文件的大小
spring.http.multipart.maxRequestSize=10Mb // 设置总上传的数据大小

10MB时默认值,而我调到100MB都没用。

贴出相关代码:

 CompletableFuture.supplyAsync(() -> {    //CompletableFuture.supplyAsync() java8 的异步调用函数
            List<FileUploadLog> fileUploadLogs = files.stream().map(file -> {
                FileOutputStream out = null;
                FileUploadLog fileUploadLog = null;
                try {
                    String filePath = StaticV.FILE_PATH + File.separator + StaticV.IMG_DIR + File.separator + LocalDate.now() + File.separator;
                    String fileName = System.currentTimeMillis()+"_"+file.getOriginalFilename();
                    File newFile = new File(filePath);
                    if (!newFile.exists()) {
                        newFile.mkdirs();
                    }
                    out = new FileOutputStream(filePath + fileName);
                    out.write(file.getBytes());    //file.getBytes() 会抛出该异常
                    String suffix = fileName.substring(fileName.lastIndexOf("."));
                    File tempFile = File.createTempFile(String.valueOf(System.currentTimeMillis()), suffix);
                    FileInputStream inputStream = new FileInputStream(filePath + fileName);
                    FileUtils.copyInputStreamToFile(inputStream, tempFile);
                    BufferedImage bufferedImage = ImageIO.read(tempFile);
                    Map<String, String> tempMap = new HashMap<>();
                    if (bufferedImage != null) {
                        tempMap.put("height", String.valueOf(bufferedImage.getHeight()));
                        tempMap.put("width", String.valueOf(bufferedImage.getWidth()));
                    }

******************************以下省略

*

解决办法:

List<String> fileNames = files.stream().map(file -> {
            String fileName = System.currentTimeMillis()+"_"+file.getOriginalFilename();
            try {
                saveFileBytesToRedis(file.getBytes(),fileName); //获取文件将文件转换成byte[]数组,进行分包存储到redis
            } catch (IOException e) {
                e.printStackTrace();
            }
            return fileName;
        }).collect(Collectors.toList());
        CompletableFuture.supplyAsync(() -> {
            List<FileUploadLog> fileUploadLogs = fileNames.stream().map(fileName -> {
                FileOutputStream out = null;
                FileUploadLog fileUploadLog = null;
                try {
                    String filePath = StaticV.FILE_PATH + File.separator + StaticV.IMG_DIR + File.separator + LocalDate.now() + File.separator;
                    File newFile = new File(filePath);
                    if (!newFile.exists()) {
                        newFile.mkdirs();
                    }
                    out = new FileOutputStream(filePath + fileName);
                    out.write(getFileBytesRedisKey(fileName)); //从redis中分包取值进行byte[]数组合并解析音频
                    String suffix = fileName.substring(fileName.lastIndexOf("."));
                    File tempFile = File.createTempFile(String.valueOf(System.currentTimeMillis()), suffix);
                    FileInputStream inputStream = new FileInputStream(filePath + fileName);
                    FileUtils.copyInputStreamToFile(inputStream, tempFile);
                    BufferedImage bufferedImage = ImageIO.read(tempFile);
                    Map<String, String> tempMap = new HashMap<>();
                    if (bufferedImage != null) {
                        tempMap.put("height", String.valueOf(bufferedImage.getHeight()));
                        tempMap.put("width", String.valueOf(bufferedImage.getWidth()));
                    }

****************以下省略

文件上传操作redis的相关方法:

    /**
     * 获取文件将文件转换成byte[]数组,进行分包存储到redis
     */
    public synchronized void saveFileBytesToRedis(byte[] buffer,String fileName)  {
        log.info("filename========================"+fileName);
        int viceLength = 180; //每个字节包大小
        int viceNumber = (int) Math.ceil(buffer.length /(double) viceLength);//存多少个包
        int from, to;
        List listrk = new ArrayList();
        for (int i=0;i<viceNumber;i++){ //将完整音频buffer[]进行循环拆分
            from=i*viceLength;
            to=from+viceLength;
            if(to>buffer.length)
                to=buffer.length;
            listrk.add(Arrays.copyOfRange(buffer,from,to));//按字节范围拷贝生成新数组,添加到List列表中
        }
        redisTemplate.opsForList().rightPushAll(StaticV.FILE_UPLOAD_FILE_BYTES + fileName, listrk);//redisTemplate的批量添加,以List列表形式进行存储
        //TODO 可用定时任务定期清理redis中的图片缓存
    }

    /**
     * 从redis中分包取值进行byte[]数组合并解析音频
     */
    public byte[] getFileBytesRedisKey(String fileName) {
        List list =redisTemplate.opsForList().range(StaticV.FILE_UPLOAD_FILE_BYTES + fileName, 0, -1); //通过key获取指定区间的值,List方式存储用List集合去接收
        //合并音频
        List<byte[]> blist = list;
        int lengthTotal = 0;
        for (byte[] item : blist) {
            lengthTotal += item.length;
        }
        byte[] totalByte = new byte[lengthTotal];
        int begin = 0;
        for (byte[] item : blist) {
            //System.arraycopy(原数组, 原数组起始位置, 目标数组, 目标数组起始位置, 复制长度);
            System.arraycopy(item, 0, totalByte, begin, item.length);
            begin += item.length;
        }
        return totalByte; //OutputStream对象输出流,直接返回为空,浏览器自动会为我们解析音频流
    }

经过这样处理,不会出现该异常。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐