【问题标题】:How to bundle multiple static files as zip archive for download如何将多个静态文件捆绑为 zip 存档以供下载
【发布时间】:2021-05-23 12:41:38
【问题描述】:

我的 Jekyll 页面旨在为编程书籍的练习提供示例解决方案。 解决方案是将一堆 .cpp 文件(C++ 代码文件)存储在我的 Jekyll 项目中的一个文件夹中,以便我可以在 IDE 中打开同一个文件夹。

我已经设法为每本书章节自动生成一页,将相关解决方案显示为代码块列表(每个练习一个)。我通过循环 site.static_files 并通过文件名中的数字识别文件来做到这一点(例如第 1 章的两个解决方案:01_1_FirstSolution.cpp01_2_SecondSolution.cpp

现在,我还想为每本书的章节提供一个 zip 存档,其中包含相关的 .cpp 文件。我不想手动制作 zip 文件,因为那样我就不能再简单地更改其中一个代码文件了。理想情况下,我想在循环 site.static_files 并过滤相关文件时构建一个 zip 文件。

在搜索这个时,我主要发现了用于捆绑和压缩资产的速度优化插件。我在 Windows 上运行 Jekyll。

【问题讨论】:

  • 一旦你对现有答案发表评论,我会做一些研究。
  • @MauricioGraciaGutierrez 我这样做了 :-)
  • 你也已经获得了赏金,很酷。
  • @MauricioGraciaGutierrez 抱歉,宽限期已接近尾声,我不想浪费积分。我给出的答案有一个有趣的转折,所以它应得的。我没有接受答案,因为问题还没有解决。如果您有其他方法,请随时回答。

标签: jekyll jekyll-extensions


【解决方案1】:

如果可以获取文件的内容(fetch 等),您可以使用 JSZip 库将它们打包成 Zip。我不确定这是否适合你在 jekyll 中......但我会尝试这样做。

要尝试示例,只需创建一个 html 文件或open my example with fetch

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title></title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.6.0/jszip.min.js" integrity="sha512-uVSVjE7zYsGz4ag0HEzfugJ78oHCI1KhdkivjQro8ABL/PRiEO4ROwvrolYAcZnky0Fl/baWKYilQfWvESliRA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    <script type="module" src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.0/FileSaver.min.js" integrity="sha512-csNcFYJniKjJxRWRV1R7fvnXrycHP6qDR21mgz1ZP55xY5d+aHLfo9/FcGDQLfn2IfngbAHd8LdfsagcCqgTcQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> 
</head>
<body>
<script>
    var zip = new JSZip();
    zip.file("Hello.txt", "Hello World\n");
    zip.file("Hello2.txt", "Hello2 World\n");
    //var img = zip.folder("images");
    //img.file("smile.gif", imgData, {base64: true});
    zip.generateAsync({type:"blob"})
    .then((content) => {
        // see FileSaver.js
        saveAs(content, "example.zip");
    });
</script>
</body>
</html>

【讨论】:

  • 也许 Ruby zip 库更适合这里,以便在 Jekyll 捆绑时生成存档。然而,作为最后的手段,在 Javascript 中即时执行它也很有趣
  • @PhilLab 我也这么认为,如果您可以跳过任何运行时工作,请跳过它 =) 我将这个库用于我的代码生成器...
  • @PhilLab 如果您允许您的客户选择文件(通过电子邮件中的复选框)这也是一个好方法。
【解决方案2】:

Jekyll 是一个主要的静态网站构建器,因此在使用 Jekyll 构建之前运行程序或 shell 脚本并将它们输出到您希望输出到上述位置的site.static_files 位置可能是理想的。

您可以在运行 Jekyll 之前使用脚本来压缩文件,然后运行 ​​Jekyll 构建过程。

您还可以编写 Generator plugins 来获得类似的结果,使用插件或 Ruby 的 system 命令在构建过程中运行脚本。

【讨论】:

  • 在 jekyll 构建过程中是否有标准的方法来执行脚本?如果我也可以使用 Jekyll 枚举静态文件 {% for file in site.static_files %} {% if file.path contains '/somesubfolder/' %} 的方式,这将是美丽的——尽管绝对不是必需品,因为无论如何我都可以在脚本中使用 ls
  • 您似乎可以使用生成器插件来运行“系统”命令。 jekyll docs 虽然我在文档上找不到太多信息,但 this issue 表明可以做到。显然这意味着从 Ruby 插件运行系统命令。
【解决方案3】:

jekyll-zip-bundler 插件。

如何使用

文件名作为多个参数:

{% zip archiveToCreate.zip file1.txt file2.txt %}

文件名中的空格:

{% zip archiveToCreate.zip file1.txt folder/file2.txt 'file with spaces.txt' %}

包含文件列表的变量也是可能的:

{% zip ziparchiveToCreate.zip {{ chapter_code_files }} %}

插件代码

# frozen_string_literal: true

# Copyright 2021 by Philipp Hasper
# MIT License
# https://github.com/PhilLab/jekyll-zip-bundler

require 'jekyll'
require 'zip'
# ~ gem 'rubyzip', '~>2.3.0'

module Jekyll
  # Valid syntax:
  # {% zip archiveToCreate.zip file1.txt file2.txt %}
  # {% zip archiveToCreate.zip file1.txt folder/file2.txt 'file with spaces.txt' %}
  # {% zip {{ variableName }} file1.txt 'folder/file with spaces.txt' {{ otherVariableName }} %}
  # {% zip {{ variableName }} {{ VariableContainingAList }} %}
  class ZipBundlerTag < Liquid::Tag
    VARIABLE_SYNTAX = /[^{]*(\{\{\s*[\w\-.]+\s*(\|.*)?\}\}[^\s{}]*)/mx.freeze
    CACHE_FOLDER = '.jekyll-cache/zip_bundler/'

    def initialize(tag_name, markup, tokens)
      super
      # Split by spaces but only if the text following contains an even number of '
      # Based on https://stackoverflow.com/a/11566264
      # Extended to also not split between the curly brackets of Liquid
      # In addition, make sure the strings are stripped and not empty
      @files = markup.strip.split(/\s(?=(?:[^'}]|'[^']*'|{{[^}]*}})*$)/)
                     .map(&:strip)
                     .reject(&:empty?)
    end

    def render(context)
      # First file is the target zip archive path
      target, files = resolve_parameters(context)
      abort 'zip tag must be called with at least two files' if files.empty?

      zipfile_path = CACHE_FOLDER + target
      FileUtils.makedirs(File.dirname(zipfile_path))

      # Create the archive. Delete file, if it already exists
      File.delete(zipfile_path) if File.exist?(zipfile_path)
      Zip::File.open(zipfile_path, Zip::File::CREATE) do |zipfile|
        files.each do |file|
          # Two arguments:
          # - The name of the file as it will appear in the archive
          # - The original file, including the path to find it
          zipfile.add(File.basename(file), file)
        end
      end
      puts "Created archive #{zipfile_path}"

      # Add the archive to the site's static files
      site = context.registers[:site]
      site.static_files << Jekyll::StaticFile.new(site, "#{site.source}/#{CACHE_FOLDER}",
                                                  File.dirname(target),
                                                  File.basename(zipfile_path))
      # No rendered output
      ''
    end

    def resolve_parameters(context)
      # Resolve the given parameters to a file list
      target, files = @files.map do |file|
        next file unless file.match(VARIABLE_SYNTAX)

        # This is a variable. Look it up.
        context[file]
      end

      [target, files]
    end
  end
end

Liquid::Template.register_tag('zip', Jekyll::ZipBundlerTag)

【讨论】:

    猜你喜欢
    • 2019-03-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-19
    • 2018-03-29
    • 1970-01-01
    • 1970-01-01
    • 2021-01-10
    相关资源
    最近更新 更多