Создайте zip-файл на S3 из файлов на S3 в Java - PullRequest
0 голосов
/ 02 июля 2019

У меня есть много файлов на S3, которые мне нужно заархивировать, а затем предоставить zip через S3.В настоящее время я архивирую их из потока в локальный файл, а затем снова загружаю файл.Это занимает много места на диске, так как каждый файл имеет около 3-10 МБ, и мне нужно сжать до 100 000 файлов.Таким образом, почтовый индекс может иметь более 1 ТБ.Поэтому я хотел бы найти решение именно по этой линии:

Создайте zip-файл на S3 из файлов на S3, используя Lambda Node

Здесь швы, zip создается напрямуюна S3, не занимая место на локальном диске.Но я просто недостаточно умен, чтобы перенести вышеуказанное решение на Java.Я также нахожу противоречивую информацию о java aws sdk, говоря, что они планировали изменить поведение потока в 2017 году.

Не уверен, поможет ли это, но вот что я делал до сих пор (Upload это моя локальная модель, которая содержит информацию S3).Я просто удалил логи и прочее для лучшей читаемости.Я думаю, что я не занимаю место для загрузки, "обвязывая" InputStream непосредственно в почтовый индекс.Но, как я уже сказал, я также хотел бы избежать локального файла zip и создать его непосредственно на S3.Однако для этого, вероятно, потребуется создать ZipOutputStream с S3 в качестве цели вместо FileOutputStream.Не уверен, как это можно сделать.

public File zipUploadsToNewTemp(List<Upload> uploads) {
    List<String> names = new ArrayList<>();

    byte[] buffer = new byte[1024];
    File tempZipFile;
    try {
      tempZipFile = File.createTempFile(UUID.randomUUID().toString(), ".zip");
    } catch (Exception e) {
      throw new ApiException(e, BaseErrorCode.FILE_ERROR, "Could not create Zip file");
    }
    try (
        FileOutputStream fileOutputStream = new FileOutputStream(tempZipFile);
        ZipOutputStream zipOutputStream = new ZipOutputStream(fileOutputStream)) {

      for (Upload upload : uploads) {
        InputStream inputStream = getStreamFromS3(upload);
        ZipEntry zipEntry = new ZipEntry(upload.getFileName());
        zipOutputStream.putNextEntry(zipEntry);
        writeStreamToZip(buffer, zipOutputStream, inputStream);
        inputStream.close();
      }
      zipOutputStream.closeEntry();
      zipOutputStream.close();
      return tempZipFile;
    } catch (IOException e) {
      logError(type, e);
      if (tempZipFile.exists()) {
        FileUtils.delete(tempZipFile);
      }
      throw new ApiException(e, BaseErrorCode.IO_ERROR,
          "Error zipping files: " + e.getMessage());
    }
}

  // I am not even sure, but I think this takes up memory and not disk space
private InputStream getStreamFromS3(Upload upload) {
    try {
      String filename = upload.getId() + "." + upload.getFileType();
      InputStream inputStream = s3FileService
          .getObject(upload.getBucketName(), filename, upload.getPath());
      return inputStream;
    } catch (ApiException e) {
      throw e;
    } catch (Exception e) {
      logError(type, e);
      throw new ApiException(e, BaseErrorCode.UNKOWN_ERROR,
          "Unkown Error communicating with S3 for file: " + upload.getFileName());
    }
}


private void writeStreamToZip(byte[] buffer, ZipOutputStream zipOutputStream,
      InputStream inputStream) {
    try {
      int len;
      while ((len = inputStream.read(buffer)) > 0) {
        zipOutputStream.write(buffer, 0, len);
      }
    } catch (IOException e) {
      throw new ApiException(e, BaseErrorCode.IO_ERROR, "Could not write stream to zip");
    }
}

И, наконец, загрузить исходный код.Inputstream создается из временного Zip-файла.

public PutObjectResult upload(InputStream inputStream, String bucketName, String filename, String folder) {
    String uploadKey = StringUtils.isEmpty(folder) ? "" : (folder + "/");
    uploadKey += filename;

    ObjectMetadata metaData = new ObjectMetadata();

    byte[] bytes;
    try {
      bytes = IOUtils.toByteArray(inputStream);
    } catch (IOException e) {
      throw new ApiException(e, BaseErrorCode.IO_ERROR, e.getMessage());
    }
    metaData.setContentLength(bytes.length);
    ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);

    PutObjectRequest putObjectRequest = new PutObjectRequest(bucketPrefix + bucketName, uploadKey, byteArrayInputStream, metaData);
    putObjectRequest.setCannedAcl(CannedAccessControlList.PublicRead);

    try {
      return getS3Client().putObject(putObjectRequest);
    } catch (SdkClientException se) {
      throw s3Exception(se);
    } finally {
      IOUtils.closeQuietly(inputStream);
    }
  }

Только что нашел вопрос, аналогичный тому, что мне нужно, также без ответа:

Загрузка ZipOutputStream в S3 без сохранения ZIP-файла (большой) временно на диск с помощью AWS S3 Java

1 Ответ

0 голосов
/ 03 июля 2019

Я бы предложил использовать экземпляр Amazon EC2 (всего 1 цент в час, или вы могли бы даже использовать точечный экземпляр, чтобы получить его по более низкой цене).Меньшие типы экземпляров стоят дешевле, но имеют ограниченную пропускную способность, поэтому поэкспериментируйте с размером, чтобы получить желаемую производительность.

Напишите скрипт для циклического просмотра файлов:

  • Скачать
  • Zip
  • Загрузка
  • Удаление локальных файлов

Вся магия zip происходит на локальном диске.Не нужно использовать потоки.Просто используйте вызовы Amazon S3 download_file() и upload_file().

Если экземпляр EC2 находится в том же регионе, что и Amazon S3, плата за передачу данных не взимается.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...