outputStream(输出流)转inputstream(输入流)以及输入流如何复用
作者:mmseoamin日期:2023-12-20

目录

      • 需求:
      • 准备:
      • 文件、流之间的转换
        • MultipartFile 转 inputstream(输入流)
        • outputStream(输出流)转为 inputstream(输入流)
        • inputstream (输入流)转 ByteArrayOutputStream
        • MultipartFile 文件直接转输入流上传和生成摘要
        • MultipartFile 文件需要转为pdf 再进行上传和生成摘要
        • 文件上传源码
            • 文件hash 摘要算法
            • docx或doc转pdf
            • 文件上传

              需求:

              通过MultipartFile 上传文件到文件服务器,上传前要把文件转为pdf格式进行上传,并生成文件摘要用来验证服务器中的文件是否被篡改。

              准备:

              需要涉及到 inputstream(输入流)或outputStream(输出流)要使用两次 。

              一、如果该文件本身就是pdf格式则直接进行上传。第一次是通过输入流去上传文件;第二次是通过输入流去生成文件摘要。

              二、如果该文件不是pdf则需要工具类把文件转为pdf再上传。转pdf的工具类 返回的为outputStream(输出流)。上传的工具类以及生成摘要的工具类则需要inputstream(输入流)。

              则需要把输出流进行转化变为输入流,然后再第一次是通过输入流去上传文件;第二次是通过输入流去生成文件摘要

              注:流读过一次就不能再读了,而InputStream对象本身不能复制

              文件、流之间的转换

              MultipartFile 转 inputstream(输入流)

               byte [] byteArr=file.getBytes();
               InputStream inputStream = new ByteArrayInputStream(byteArr);
              

              outputStream(输出流)转为 inputstream(输入流)

               ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
               InputStream inputStream2 = new ByteArrayInputStream(outputStream.toByteArray());
              

              inputstream (输入流)转 ByteArrayOutputStream

               //InputStream 转 ByteArrayOutputStream
               //获取到一个inputstream后,可能要多次利用它进行read的操作。由于流读过一次就不能再读了,而InputStream对象本身不能复制,而且它也没有实现Cloneable接口  
               public static ByteArrayOutputStream cloneInputStream(InputStream input) {
                      try {
                          ByteArrayOutputStream baos = new ByteArrayOutputStream();
                          byte[] buffer = new byte[1024];
                          int len;
                          while ((len = input.read(buffer)) > -1) {
                              baos.write(buffer, 0, len);
                          }
                          baos.flush();
                          return baos;
                      } catch (IOException e) {
                          e.printStackTrace();
                          return null;
                      }
                  }
              

              MultipartFile 文件直接转输入流上传和生成摘要

              通过file 转字节数组,因为流不能重复读,所以要new成两个输入流。

               //获取并生成以pdf为后缀的文件名称
                String fileName = StringUtils.substringBeforeLast(originalFilename,".");
                 fileName = fileName +".pdf";
                 //如果上传的为pdf 则直接进行上传 不需要转换
                 if(originalFilename.endsWith(".pdf")){
                     //文件转字节数组
                     byte [] byteArr=file.getBytes();
                     //输入流1
                     InputStream inputStream = new ByteArrayInputStream(byteArr);
                     //输入流2
                     InputStream inputStream2 = new ByteArrayInputStream(byteArr);
                     //文件上传
                     url = CephUtils.uploadInputStreamReturnUrl("/" + Constants.CEPH_BUCK_NAME, fileName, inputStream);
                     //生成文档hash 摘要
                     hash = FileHahUtil.hashAbstractByInputStream(inputStream2);
                  }
              

              MultipartFile 文件需要转为pdf 再进行上传和生成摘要

               //转换为pdf 后的输出流
               ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
               //原文件
               byte [] byteArr=file.getBytes();
                InputStream inputStream = new ByteArrayInputStream(byteArr);
                //要转为pdf 文档
                Word2PdfUtil.convert2PdfStream(inputStream,outputStream);
                //输出流转输入流
                ByteArrayOutputStream baosPdf = (ByteArrayOutputStream) outputStream ;
                InputStream inputStream2 = new ByteArrayInputStream(baosPdf.toByteArray());
                //inputStream 只能用来读取一次  所以进行copy一个新的 用来生成摘要
                InputStream inputStream3 = new ByteArrayInputStream(baosPdf.toByteArray());
                //生成文档hash 摘要
                hash = FileHahUtil.hashAbstractByInputStream(inputStream3);
                //上传文件
                url = fileService.uploadFileByInputStream(inputStream2,fileName);
              

              文件上传源码

               //文件上传(文档转pdf)
                  @ApiOperation(value = "文件上传(文档转pdf)", produces = "application/json")
                  @ApiResponses(value = {@ApiResponse(code = 200, message = "文件上传(文档转pdf)")})
                  @PostMapping(value = "/uploadWordFile")
                  public BaseVo uploadWordFile(HttpServletRequest request, MultipartFile file, HttpServletResponse response){
                      long startTimeTotal = System.currentTimeMillis();
                      BaseVo baseVo = new BaseVo();
                      baseVo.setCodeMessage(CodeConstant.FAILURE_CODE);
                      try {
                          if(null != file){
                              String originalFilename = file.getOriginalFilename();
                              //文件上传后返回的地址
                              String url = "";
                              //文件摘要的hash值
                              String hash = "";
                              if (!originalFilename.endsWith(".docx") && !originalFilename.endsWith(".doc") && !originalFilename.endsWith(".pdf")) {
                                  //上传的文件格式不支持
                                  baseVo.setMessage("暂不支持当前文件格式上传!");
                              }else {
                                  //生成新的文件名称
                                  String fileName = StringUtils.substringBeforeLast(originalFilename,".");
                                  fileName = fileName +".pdf";
                                  //如果上传的为pdf 则直接进行上传 不需要转换
                                  if(originalFilename.endsWith(".pdf")){
                                      byte [] byteArr=file.getBytes();
                                      InputStream inputStream = new ByteArrayInputStream(byteArr);
                                      InputStream inputStream2 = new ByteArrayInputStream(byteArr);
                                      url = CephUtils.uploadInputStreamReturnUrl("/" + Constants.CEPH_BUCK_NAME, fileName, inputStream);
                                      //生成文档hash 摘要
                                      hash = FileHahUtil.hashAbstractByInputStream(inputStream2);
                                  }else {
                                      ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                                      byte [] byteArr=file.getBytes();
                                      InputStream inputStream = new ByteArrayInputStream(byteArr);
                                      //要转为pdf 文档
                                      Word2PdfUtil.convert2PdfStream(inputStream,outputStream);
                                      ByteArrayOutputStream baosPdf = (ByteArrayOutputStream) outputStream ;
                                      InputStream inputStream2 = new ByteArrayInputStream(baosPdf.toByteArray());
                                      //inputStream 只能用来读取一次  所以进行copy一个新的 用来生成摘要
                                      InputStream inputStream3 = new ByteArrayInputStream(baosPdf.toByteArray());
                                      //生成文档hash 摘要
                                      hash = FileHahUtil.hashAbstractByInputStream(inputStream3);
                                      url = fileService.uploadFileByInputStream(inputStream2,fileName);
                                      baosPdf.close();
                                      inputStream2.close();
                                      outputStream.close();
                                  }
                                  if(StringUtils.isNotEmpty(url)){
                                      // 保存合同信息 到数据库
                                      Contract contract = new Contract();
                                      //随机字符串
                                      String str = StringUtils.replace(UUID.randomUUID().toString(), "-", "");
                                      contract.setCode(CodeGenerator.getLongCode(str));
                                      contract.setContractUrl(url);
                                      contract.setName(fileName);
                                      contract.setHashCode(hash);
                                      contractService.saveOrUpdate(contract);
                                      //返回合同信息
                                      baseVo.setData(contract);
                                      baseVo.setCodeMessage(CodeConstant.SUCCESS_CODE);
                                  }
                              }
                          }
                      }catch (Exception e){
                          e.printStackTrace();
                          MeUtils.info("uploadFile error",e);
                      }
                      long endTimeTotal = System.currentTimeMillis();
                      MeUtils.info("uploadFile total time:" + (endTimeTotal - startTimeTotal));
                      return baseVo;
                  }
              

              生成文件摘要用的是文件hash 摘要算法中的SHA-256,docx或doc转pdf用的是aspose 中提供的方法,文件上传用的Ceph分布式文件系统中的。这里暂时不详细介绍,只贴一些代码。后面再出详细文档,以及开发中遇到的坑。

              文件hash 摘要算法
               /**
                   * 生成文件hashCode值
                   */
                  public static String hashAbstractByInputStream(InputStream fis) throws Exception {
                      String sha256 = null;
                      try {
                          MessageDigest md = MessageDigest.getInstance("SHA-256");
                          byte buffer[] = new byte[1024];
                          int length = -1;
                          while ((length = fis.read(buffer, 0, 1024)) != -1) {
                              md.update(buffer, 0, length);
                          }
                          byte[] digest = md.digest();
                          sha256 = byte2hexLower(digest);
                      } catch (Exception e) {
                          e.printStackTrace();
                          throw new Exception("生成文件hash值失败");
                      } finally {
                          try {
                              if (fis != null) {
                                  fis.close();
                              }
                          } catch (IOException e) {
                              e.printStackTrace();
                          }
                      }
                      return sha256;
                  }
              
              docx或doc转pdf
              public static void convert2PdfStream(InputStream inputStream, ByteArrayOutputStream outputStream) {
                      if (!getLicense()) { // 验证License 若不验证则转化出的pdf文档会有水印产生
                          return;
                      }
                      try {
                          long old = System.currentTimeMillis();
                          Document doc = new Document(inputStream);
                          //insertWatermarkText(doc, "测试水印"); //添加水印
                          PdfSaveOptions pdfSaveOptions = new PdfSaveOptions();
                          pdfSaveOptions.setSaveFormat(SaveFormat.PDF);
                          // 设置3级doc书签需要保存到pdf的heading中
                          pdfSaveOptions.getOutlineOptions().setHeadingsOutlineLevels(3);
                          // 设置pdf中默认展开1级
                          pdfSaveOptions.getOutlineOptions().setExpandedOutlineLevels(1);
                          //doc.save(outputStream, pdfSaveOptions);
                          // 全面支持DOC, DOCX, OOXML, RTF HTML, OpenDocument, PDF,EPUB, XPS, SWF 相互转换
                          doc.save(outputStream, SaveFormat.PDF);
                          long now = System.currentTimeMillis();
                          System.out.println("共耗时:" + ((now - old) / 1000.0) + "秒"); // 转化用时
                      } catch (Exception e) {
                          e.printStackTrace();
                      }
                  }
              
              文件上传
               /**
                   * 上传InputStream文件
                   *
                   * @param bucketName
                   * @param fileName
                   * @param input
                   */
                  public static String uploadInputStreamReturnUrl(String bucketName, String fileName, InputStream input) {
              //        String path = bucketName + timeSuffix();
                      PutObjectResult putObjectResult = conn.putObject(bucketName, fileName, input, new ObjectMetadata());
                      String cephUrl = ENDPOINT + bucketName + "/" + fileName;
                      return cephUrl;
                  }