用java编写的Azure函数抛出FailureException:OutOfMemoryError:java堆空间堆栈,同时解压缩文件大小>;80MB

用java编写的Azure函数抛出FailureException:OutOfMemoryError:java堆空间堆栈,同时解压缩文件大小>;80MB,java,unzip,azure-blob-storage,azure-function-app,azure-blob-trigger,Java,Unzip,Azure Blob Storage,Azure Function App,Azure Blob Trigger,我有一个用java编写的Azure函数,它将侦听Azure上的队列消息,队列消息具有Azure blob容器上zip文件的路径,一旦收到队列消息,它将从Azure上的路径位置获取zip文件并解压缩到Azure上的容器。它适用于小文件,但大于80MB时会显示FailureException:OutOfMemoryError:Java heap spaceStackexception。我的代码如下 @FunctionName("queueprocessor") public vo

我有一个用java编写的Azure函数,它将侦听Azure上的队列消息,队列消息具有Azure blob容器上zip文件的路径,一旦收到队列消息,它将从Azure上的路径位置获取zip文件并解压缩到Azure上的容器。它适用于小文件,但大于80MB时会显示
FailureException:OutOfMemoryError:Java heap spaceStack
exception。我的代码如下

@FunctionName("queueprocessor")
public void run(@QueueTrigger(name = "msg",
                              queueName = "queuetest",
                              dataType = "",
                              connection = "AzureWebJobsStorage") Details message,
                final ExecutionContext executionContext,
                @BlobInput(name = "file", 
                           dataType = "binary", 
                           connection = "AzureWebJobsStorage",
                           path = "{Path}") byte[] content) {

  executionContext.getLogger().info("PATH: " + message.getPath());

  CloudStorageAccount storageAccount = null;
  CloudBlobClient blobClient = null;
  CloudBlobContainer container = null;

  try {

    String connectStr = "DefaultEndpointsProtocol=https;AccountName=name;AccountKey=mykey;EndpointSuffix=core.windows.net";

    //unique name of the container
    String containerName = "output";

    // Config to upload file size > 1MB in chunks
    int deltaBackoff = 2;
    int maxAttempts = 2;
    BlobRequestOptions blobReqOption = new BlobRequestOptions();
    blobReqOption.setSingleBlobPutThresholdInBytes(1024 * 1024); // 1MB
    blobReqOption.setRetryPolicyFactory(new RetryExponentialRetry(deltaBackoff, maxAttempts));

    // Parse the connection string and create a blob client to interact with Blob storage
    storageAccount = CloudStorageAccount.parse(connectStr);
    blobClient = storageAccount.createCloudBlobClient();
    blobClient.setDefaultRequestOptions(blobReqOption);
    container = blobClient.getContainerReference(containerName);

    container.createIfNotExists(BlobContainerPublicAccessType.CONTAINER, new BlobRequestOptions(), new OperationContext());

    ZipInputStream zipIn = new ZipInputStream(new ByteArrayInputStream(content));

    ZipEntry zipEntry = zipIn.getNextEntry();
    while (zipEntry != null) {
      executionContext.getLogger().info("ZipEntry name: " + zipEntry.getName());

      //Getting a blob reference
      CloudBlockBlob blob = container.getBlockBlobReference(zipEntry.getName());

      ByteArrayOutputStream outputB = new ByteArrayOutputStream();
      byte[] buf = new byte[1024];
      int n;
      while ((n = zipIn.read(buf, 0, 1024)) != -1) {
        outputB.write(buf, 0, n);
      }

      // Upload to container
      ByteArrayInputStream inputS = new ByteArrayInputStream(outputB.toByteArray());

      blob.setStreamWriteSizeInBytes(256 * 1024); // 256K

      blob.upload(inputS, inputS.available());

      executionContext.getLogger().info("ZipEntry name: " + zipEntry.getName() + " extracted");
      zipIn.closeEntry();
      zipEntry = zipIn.getNextEntry();
    }
    zipIn.close();

    executionContext.getLogger().info("FILE EXTRACTION FINISHED");

  } catch(Exception e) {
    e.printStackTrace();
  }
}

详细信息消息
具有ID和文件路径,路径作为输入提供给
@BlobInput(…,path={path},…)
。根据我的分析,我觉得
@BlobInput
正在将完整的文件加载到内存中,这就是我从内存错误中得到
的原因。如果我是对的,请告诉我还有什么办法可以避免吗?。因为将来文件大小可能会达到2GB。如果在解压代码中有任何错误,请告诉我。谢谢。

我将@Joachimauer的建议总结如下

当我们使用Azure Function blob存储绑定来处理java Function应用程序中的Azure blob内容时,它会将整个内容保存在内存中。使用它来处理大文件,我们可能会遇到
OutOfMemoryError
。因此,如果我们想要处理大型azure blob,我们应该使用blob sdk打开一个输入流,然后使用该流处理内容

比如说

SDK


com.azure


有关更多详细信息,请参阅。

我将@Joachimauer的建议总结如下

当我们使用Azure Function blob存储绑定来处理java Function应用程序中的Azure blob内容时,它会将整个内容保存在内存中。使用它来处理大文件,我们可能会遇到
OutOfMemoryError
。因此,如果我们想要处理大型azure blob,我们应该使用blob sdk打开一个输入流,然后使用该流处理内容

比如说

SDK


com.azure


有关更多详细信息,请参阅。

您同时将zip文件(在
内容中)和解压缩文件(在
输出中)完全保存在内存中。我不知道是否有办法将
内容
作为流获取,但至少解压后的文件本身,您应该能够直接从流获取,而不是完全解压(假设CloudBlockBlob可以在不将其完全加载到内存的情况下使用流)。谢谢,我刚刚设法直接从URL流中获取
内容
,效果很好。@JoachimSauer您能将您的建议作为解决方案发布吗?它可能会帮助更多有类似问题的人。您可以同时将zip文件(在
内容中
)和解压缩文件(在
输出中
)完全保存在内存中。我不知道是否有办法将
内容
作为流获取,但至少解压后的文件本身,您应该能够直接从流获取,而不是完全解压(假设CloudBlockBlob可以在不将其完全加载到内存的情况下使用流)。谢谢,我刚刚设法直接从URL流中获取
内容
,效果很好。@JoachimSauer您能将您的建议作为解决方案发布吗?它可能会帮助更多有类似问题的人。谢谢你的全部代码。您能告诉我您尝试的文件大小以及azure或本地计算机上的堆大小吗。@brijeshPatil我的zip文件是189MB。在我的测试环境中,初始heapsize是134217728,最大heapsize是2118123520。好的,谢谢。真的很有帮助。@brijeshPatil如果对你有帮助,你可以吗?Iy可能会帮助更多有类似问题的人。我需要在azure环境中测试这一点。由于azure上的堆大小非常小,我们将对其进行测试,然后肯定会接受它。感谢您提供完整的代码。您能告诉我您尝试的文件大小以及azure或本地计算机上的堆大小吗。@brijeshPatil我的zip文件是189MB。在我的测试环境中,初始heapsize是134217728,最大heapsize是2118123520。好的,谢谢。真的很有帮助。@brijeshPatil如果对你有帮助,你可以吗?Iy可能会帮助更多有类似问题的人。我需要在azure环境中测试这一点。因为azure上的堆大小非常小,所以我们将测试它,然后肯定会接受它。
    <dependency>
          <groupId>com.azure</groupId>
          <artifactId>azure-storage-blob</artifactId>
          <version>12.9.0</version>
      </dependency>
 String accountName="";
        String accountKey="";
        StorageSharedKeyCredential sharedKeyCredential =
                new StorageSharedKeyCredential(accountName, accountKey);

        BlobServiceClient blobServiceClient = new BlobServiceClientBuilder()
                               .credential(sharedKeyCredential)
                               .endpoint("https://" + accountName + ".blob.core.windows.net")
                               .buildClient();
        BlobContainerClient desContainerClient = blobServiceClient.getBlobContainerClient("output");
        BlobContainerClient sourceContainerClient = blobServiceClient.getBlobContainerClient("upload");
        BlobInputStreamOptions option = new BlobInputStreamOptions();
        //The size of each data chunk returned from the service
        option.setBlockSize(1024*1024);
        ZipInputStream zipInput = null;
        try {

            zipInput= new ZipInputStream( sourceContainerClient.getBlobClient("<read file name deom queue message>").openInputStream(option));
            ZipEntry zipEntry= zipInput.getNextEntry();
            while(zipEntry != null){
                System.out.println("ZipEntry name: " + zipEntry.getName());
                BlobOutputStream outputB = desContainerClient.getBlobClient(zipEntry.getName()).getBlockBlobClient().getBlobOutputStream();
                byte[] bytesIn = new byte[1024*1024];
                int read = 0;
                while ((read = zipInput.read(bytesIn)) != -1) {
                    outputB.write(bytesIn, 0, read);
                }
                outputB.flush();
                outputB.close();
                zipInput.closeEntry();
                zipEntry =zipInput.getNextEntry();
            }

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                zipInput.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }