如何在Google云存储中解压.zip文件?

38

我该如何在Google Cloud Storage Bucket中解压.zip文件?(如果我们有其他工具,如AWS的“CloudBerry Explorer”,那就太好了。)


2
你有没有找到一个好的解决方案? - chinabuffet
12个回答

20

您可以使用Python,例如从云函数中:

    from google.cloud import storage
    from zipfile import ZipFile
    from zipfile import is_zipfile
    import io

    def zipextract(bucketname, zipfilename_with_path):

        storage_client = storage.Client()
        bucket = storage_client.get_bucket(bucketname)

        destination_blob_pathname = zipfilename_with_path
        
        blob = bucket.blob(destination_blob_pathname)
        zipbytes = io.BytesIO(blob.download_as_string())

        if is_zipfile(zipbytes):
            with ZipFile(zipbytes, 'r') as myzip:
                for contentfilename in myzip.namelist():
                    contentfile = myzip.read(contentfilename)
                    blob = bucket.blob(zipfilename_with_path + "/" + contentfilename)
                    blob.upload_from_string(contentfile)

    zipextract("mybucket", "path/file.zip") # if the file is gs://mybucket/path/file.zip

1
嗨,丹尼尔,我的代码无法运行,无法进入if条件语句。 - Bhagesh Arora
将此复制粘贴到Colab笔记本中,只需添加验证并指定项目,它就可以完美运行。 - Sylvain Gantois
嘿,我一直在尝试使用我的存储桶 gs://frames_final/frames_final_cat.zip 和函数 zipextract("frames_final", "frames_final_cat.zip") ,但是出现了一个错误,说项目没有传递到环境中。 - yudhiesh

14

这是我创建的一些代码,可作为Firebase云函数运行。 它旨在监听加载到具有content-type 'application/zip'的存储桶中的文件,并将其原地提取。

    const functions = require('firebase-functions');
    const admin = require("firebase-admin");
    const path = require('path');
    const fs = require('fs');
    const os = require('os');
    const unzip = require('unzipper')

    admin.initializeApp();

    const storage = admin.storage();


    const runtimeOpts = {
      timeoutSeconds: 540,
      memory: '2GB'
    }

    exports.unzip = functions.runWith(runtimeOpts).storage.object().onFinalize((object) => {

        return new Promise((resolve, reject) => {
            //console.log(object)
            if (object.contentType !== 'application/zip') {
              reject();
            } else {
              const bucket = firebase.storage.bucket(object.bucket)
              const remoteFile = bucket.file(object.name)
              const remoteDir = object.name.replace('.zip', '')

              console.log(`Downloading ${remoteFile}`)

              remoteFile.createReadStream()
                .on('error', err => {
                  console.error(err)
                  reject(err);
                })
                .on('response', response => {
                  // Server connected and responded with the specified status and headers.
                  //console.log(response)
                })
                .on('end', () => {
                  // The file is fully downloaded.
                  console.log("Finished downloading.")
                  resolve();
                })
                .pipe(unzip.Parse())
                .on('entry', entry => {
                  const file = bucket.file(`${remoteDir}/${entry.path}`)

                  entry.pipe(file.createWriteStream())
                  .on('error', err => {
                    console.log(err)
                    reject(err);
                  })
                  .on('finish', () => {
                    console.log(`Finsihed extracting ${remoteDir}/${entry.path}`)
                  });

                  entry.autodrain();

                });
            }
        })

    });

4
感谢您的示例,但我发现了一个严重的缺陷:如果您消费了流,就应该仅在不消费流时调用entry.autodrain()。否则,您将会有损坏的输出文件。在我修改代码之前我也犯了这个错误。 - Anders Emil
我可以补充一下,你应该将 firebase.storage.bucket(object.bucket) 替换为 admin.storage.bucket(),因为 firebase 未定义。 - Arkady

11

如果你因为使用gsutil cp命令从另一台服务器移动大文件而在Google Cloud Storage存储桶上有一个zip文件,你可以在复制时对其进行gzip压缩,这样它就会以压缩格式传输并在到达存储桶时进行解压。

可以通过使用-Z参数在gsutil cp中内置实现此功能。

例如:

gsutil cp -Z largefile.txt gs://bucket/largefile.txt

1
如果我正确理解 OP 的问题,他们正在寻找一种工具,可以上传一个压缩文件,然后在存储桶中解压它。gsutil cp -Z 并不能做到这一点。它会将文件压缩并保留在存储桶中。来源:https://cloud.google.com/storage/docs/gsutil/commands/cp#synchronizing-over-os-specific-file-types-such-as-symlinks-and-devices - urig
1
已确认此回答未解决原问题,且未能执行回答中描述的行为。 - jwan

11
在Shell中,您可以使用以下命令来解压缩一个压缩文件。
gsutil cat gs://bucket/obj.csv.gz | zcat |  gsutil cp - gs://bucket/obj.csv

嗨@Dishant,我该如何在Google Cloud SDK工具中使用zcat? “'zcat'不被识别为内部或外部命令” - Abdurrahman I.
@AbdurrahmanI.目前Google Cloud SDK中没有提供解压缩文件的功能。我发现最简单的方法是使用这个shell命令。 - Dishant Mishra
如果 zip 文件中包含多个文件,那么我们如何解压缩所有这些文件(我们在目标位置指定一个名字-file.csv),例如:gsutil cat gs://bucket/csv_files.zip | unzip | gsutil cp - gs://bucket/one_file.csv。 这可以运行,但我们看不到存在于 csv_files.zip 中的多个文件。 - Bhagesh Arora
@BhageshArora 这个命令无法用于多个文件被压缩在一起的情况,因为它会将输出重定向到单个文件。您可能需要下载该文件,解压缩并将提取的文件复制到 GCS 中。 如果已安装 JDK,则可以使用以下命令直接下载和解压缩文件。 gsutil cat gs://bucket/obj.zip | jar xvf /dev/stdin - Dishant Mishra
我曾经为了创建多个进程而苦苦挣扎,但最终找到了一个简单的解决方案。你可能会感兴趣。 gsutil cat gs://bucket/*/*/*/*.gz | zcat | gsutil cp - gs://bucket/decompressed/result.json - Abdurrahman I.
这是流式解压缩、上传吗?还是先下载整个文件,然后应用解压缩,最后再上传回GCS? - Ashika Umanga Umagiliya

8

Google Cloud Dataflow有数据流模板,可以帮助在云存储中压缩/解压文件。请参考下面的截屏

该模板实现了一个批处理管道,将Cloud Storage上的压缩文件解压到指定位置。当您想要使用压缩数据以最小化网络带宽成本时,此功能非常有用。 在单个执行期间,管道会自动处理多种压缩模式,并根据文件扩展名(.bzip2、.deflate、.gz、.zip)确定要使用的解压缩模式。

管道要求

要解压缩的文件必须是以下格式之一:Bzip2、Deflate、Gzip、Zip。

在管道执行之前,输出目录必须存在。


3
仅适用于单个文件夹,当其中包含多个子目录时不适用。 - yudhiesh

8

GCS中没有解压文件的机制。有一个关于此问题的功能请求已经转发给了Google开发团队。

作为替代方案,您可以将ZIP文件上传到GCS存储桶中,然后将它们下载到附加到VM实例的持久性磁盘上,在那里解压缩它们,并使用gsutil工具上传未压缩的文件。


1
如何在附加到VM实例的持久磁盘上解压缩zip文件? - Doena
虚拟机实例不会有文件大小限制吗? - BenKoshy
该问题被标记为已阻止,一段时间后解除阻止,然后关闭。这里是另一个功能请求。 - Oscar Barlow

3
  1. 在您的gcloud控制台中启用Dataflow API
  2. 在您的存储桶中创建一个temp目录(不能使用根目录)。
  3. 在下面的命令中替换YOUR_REGION(例如europe-west6)和YOUR_BUCKET,然后使用gcloud cli运行该命令(假设gz文件位于根目录 - 如果不是,请更改):
gcloud dataflow jobs run unzip \
--gcs-location gs://dataflow-templates-YOUR_REGION/latest/Bulk_Decompress_GCS_Files \
--region YOUR_REGION \
--num-workers 1 \
--staging-location gs://YOUR_BUCKET/temp \
--parameters inputFilePattern=gs://YOUR_BUCKET/*.gz,outputDirectory=gs://YOUR_BUCKET/,outputFailureFile=gs://YOUR_BUCKET/decomperror.txt

4
注意:这将把压缩文件中的所有文件合并成一个输出文件。 - Frank Blechschmidt

2

很抱歉,在Google Cloud中默认情况下,没有任何程序可以实现这个功能...但是你可以通过使用Python等工具来实现。

在任何安装了Python的机器上都可以使用的通用方法(因此也适用于Google Cloud):

您需要输入以下命令:

python

或者如果您需要管理员权限:

sudo python

然后在 Python 解释器 中:

>>> from zipfile import ZipFile
>>> zip_file = ZipFile('path_to_file/t.zip', 'r')
>>> zip_file.extractall('path_to_extract_folder')

最后,按下Ctrl+D退出Python解释器

解压后的文件将位于您指定的位置(当然,如果您有这些位置的适当权限)。

以上方法在Python 2Python 3中均可使用。

尽情享受吧!:)


我不明白为什么这个答案会得到负分。当我添加这个答案时,我尽可能地使它有用、正确且与其他答案不同。如果有人在评论中告诉我哪里出了问题,我将不胜感激。谢谢! - simhumileco
2
我先测试了你的解决方案,因为它似乎比被接受的更容易。然而,在我指定路径 gs://mybucket/myfolder/myfile.zip 的第二行时就失败了。我认为 ZipFile 不能直接访问桶中的文件。 - Sylvain Gantois
谢谢您的评论,@SylvainGantois。事实上,我的解决方案假设我们可以在常规文件夹结构内访问zip包。我希望它对至少一些用户有用,就像对我一样。 - simhumileco
8
问题的关键是要从Google Storage解压缩和压缩文件,而不是在本地文件系统上解压缩。如果您在具有本地文件系统上的文件的VM上操作,那么最好直接使用unzip命令。 - de1
1
只是评论一下,有一个名为gcsfs的包,可以使您像使用普通文件系统一样引用gcs存储桶文件。 - Ian Wesley

1

您可以使用Cloud Storage触发器创建Google Cloud Function。

当新对象被创建时,该函数将被触发。

const functions = require("@google-cloud/functions-framework");
const {Storage} = require("@google-cloud/storage");
const unzip = require("unzip-stream");

functions.cloudEvent("gcs-unzip", async cloudEvent => {

    //console.log(JSON.stringify(cloudEvent, null, 4));

    const zipFile = cloudEvent.data;

    //console.log(JSON.stringify(file, null, 4));

    if (zipFile.contentType === "application/zip") {
        const storage = new Storage();

        async function unzipAndUploadContainedFiles() {
            await storage
                .bucket(zipFile.bucket)
                .file(zipFile.name)
                .createReadStream()
                .pipe(unzip.Parse())
                .on("entry", async function (entry) { //there could be multiple files and even a directory structure in the zip file
                    //console.log(JSON.stringify(entry, null, 4));

                    const gcsTargetFileName = zipFile.name.replace(".zip", "") + "/" + entry.path;
                    if (entry.type === "File") {
                        entry.pipe(storage.bucket(zipFile.bucket).file(gcsTargetFileName).createWriteStream());
                    }
                });
        }

        await unzipAndUploadContainedFiles().catch(err => {
            console.error(err);
        });

    } else {
        console.log("Non-zip file ignored.");
    }

});

1

另一个快速的方法是使用版本3.2或更高版本Python

import shutil
shutil.unpack_archive('filename')

该方法还允许您指定目标文件夹:
shutil.unpack_archive('filename', 'extract_dir')

上述方法不仅适用于zip存档,还适用于targztarbztarxztar存档。
如果您需要更多选项,请查看shutil模块的文档:shutil.unpack_archive

1
刚测试过了,运行非常顺畅。不确定为什么之前会得到负面评价。我认为这是本主题中最佳的解决方案。 - Marc
@Marc 因为它不适用于问题中的 Google Cloud 文件。 - YGao

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接