java—将文本pdf直接保存到s3,而不必在本地保存

u0sqgete  于 2021-06-29  发布在  Java
关注(0)|答案(2)|浏览(587)

我使用itext 5和java生成pdf文件,并将它们保存在本地,然后将这些本地保存的文件保存在awss3上。有没有一种方法可以直接将它们发送到s3,而不必在本地保存它们。我见过一些例子,但没有一个对我有用。
这就是我生成pdf文件的方式

String path = //local directory on my computer
 Document document = new Document();
 PdfWriter pdfWriter = PdfWriter.getInstance(document, new FileOutputStream(path));
 document.open();

 // add text to document
 document.close();

我就是这样把它保存在s3上的

public void saveFileToS3(String pathLocal, String pathAws) {

    // init aws 

    PutObjectRequest objectRequest = PutObjectRequest.builder()
        .bucket(bucketName)
        .key(folderName + "/" + pathAws)
        .build();

    CompletableFuture<PutObjectResponse> future = s3Client.putObject(objectRequest,
        AsyncRequestBody.fromFile(Paths.get(pathLocal))
    );

    future.whenComplete((resp, err) -> {
      try {
        if (resp != null) {
          System.out.println("Object uploaded. Details: " + resp);
        } else {
          err.printStackTrace();
        }
      } finally {
        s3Client.close();
      }
    });

    future.join();
  }

string pathlocal是我在本地保存文件的路径,而string pathaws是s3上保存文件的路径。

iyfamqjs

iyfamqjs1#

首先是旁注: new FileOutputStream(path) 你的代码是错误的;如果不通过try-with-resources保护资源,就不能创建新资源(比如表示实际资源的流/读/写器:网络上的套接字、磁盘上的文件等等)。
不过,一般来说,你不希望这里有一个fos。您希望有一个流,使任何字节产生的pdf生成代码和发送通过 thatOutputStream.write 直接放到网络上。
这有点可能,但在这种情况下并不容易。
首先,让我解释一下这些库所关心的问题,以便您完全理解为什么这不太容易,并且您可以因此判断各种解决方案是否适合您的特定项目。
问题的核心是,您有两个独立的进程,每个进程都依赖于另一个进程。
pdf gen代码想要生成pdf字节,并且想要完全的自由:如果它需要查询数据库作为工作的一部分,它希望能够做到这一点。但是,它受到输出通道的限制:理论上,它可能每秒生成许多GB:如果输出通道“已满”,它希望最终停止自己(阻止其线程,或者将控制权放弃给正在处理其输出的任何对象,以便可以处理一堆输出)。例如,如果您的磁盘可以以1gb/s的速度存储,而您的pdf代码正在生成无限大小的pdf,并且可以以2gb/s的速度进行存储,则pdf代码需要减慢速度。
输出通道代码(在本例中是awss3 putobject代码)也希望有阻塞的自由:如果网络缓冲区已满,它必须等待:毕竟,它将数据包从计算机背面推出的速度是有限的。它还受到输入的限制:如果pdf gen代码以1gb/s的速度生成,但是aws s3 putobject代码可以以2gb/s的速度发送,那么putobject代码必须减慢自己的速度;如果没有要发送的字节,它将无法发送更多的字节。
通常在java代码中,模型非常简单:一方(生产者或消费者)被认为是控制的,而不是瓶颈。例如,如果您有生成无限个零并将其写入磁盘的代码:

byte[] allZeroes = new byte[60000];
try (FileOutputStream fos = new FileOutputStream("test.dat")) {
    while (true) fos.write(allZeroes);
}

非常简单的代码。但是,请注意,这里控制的“生产者”一方实际上是有障碍的。这个 write 方法块—如果磁盘正忙于处理,则 write 方法不会立即返回。当磁盘忙于处理所有数据时,cpu处于空闲状态。它本可以花时间制造更多的零!
在这个例子中,这是愚蠢的-中央处理器可以产生零难以置信的快,生产代码是许多数量级快于'消费'代码,这是有道理的生产方只是冷静了一点,因为消费者正忙于处理这一切。
但是想象一下工作方式有点不同的代码:与其编写源源不断的零,不如想象一下挖掘比特币的代码,并编写挖掘块(每个块价值数千美元,这应该表明它们生成的速度有多慢)。一个月一次已经非常令人印象深刻了)。很明显,这个障碍在这个意义上是愚蠢的:cpu应该忙于挖掘比特币,而不是在等待磁盘时摆弄拇指。在这种情况下,您希望两个进程(或者至少是较慢的进程,在本例中是生产者)永远不要等待另一方。不应该让瓶颈等待。
忙于生成pdf数据的代码在希望将数据发送到putobject代码时正在进行for循环,而putobject代码在希望获得更多pdf数据时正在计算数据段上的哈希。如果任何一方都不应该在另一方忙的时候转动拇指,那么除了拥有2根光纤(有效的是stacktraces)并让这些光纤互相传递数据之外,就没有别的出路了。在java中,这必须通过线程来完成——ProjectLoom即将面世,它将为您提供有趣的单核选项,但是ProjectLoom还不是java的一部分。
PutObjectCode就是围绕这个想法专门设计的;通常,输出通过返回 OutputStream ; Files.newOutputStream 返回1,也返回1 new FileOutputStream() ,也是 socket.getOutputStream ,也一样 servletHttpResponse.getOutputStream() . 但不是对象:它不返回任何东西;它需要一个输入流。类似地,pdf代码也不返回任何内容,它需要一个outputstream。
因此,困境。
不过,线程解决方案非常简单。你需要一个线程来生成pdf,一个线程来发送给aws。你用管道把两者连接起来。
另一种解决方案是回到模型中,一方只需等待一段时间,然后摆弄自己的大拇指,但awsapi不支持它。下面是一个要点,它试图通过使用multipart特性来提供给您。

k75qkfdt

k75qkfdt2#

所以我找到了一种方法,我把itext文件转换成字节数组,然后把pdf文件上传成字节数组

Document document = new Document();
 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

 PdfWriter.getInstance(document, byteArrayOutputStream);

 document.open();

//add stuff to pdf

 document.close();

//convert it into a byte array
byte[] pdfBytes = byteArrayOutputStream.toByteArray()

当上传到s3时,我传递的是字节,而不是像以前那样传递文件路径

CompletableFuture<PutObjectResponse> future = s3Client.putObject(objectRequest,
        AsyncRequestBody.fromBytes(pdfBytes)
    );

相关问题