Ruby Zip:在打开写入时无法打开条目进行阅读

时间:2018-04-11 19:00:59

标签: ruby docx

我尝试编写一些邮件合并代码,我打开一个docx文件(作为zip)用数据替换标签,然后创建一个新的docx文件(作为zip)并迭代旧的zip文件,添加新的替换数据或从旧的docx文件中提取现有文件,然后添加它。

我遇到的问题是,当我尝试访问out.get_output_stream方法时,我收到以下错误:

cannot open entry for reading while its open for writing - [Content_Types].xml (StandardError)

[Content_Types].xml恰好是docx中的第一个文件,这就是为什么它会对该特定文件进行轰炸的原因。我做错了什么?

require 'rubygems'
require 'zip' # rubyzip gem

class WordMailMerge
  def self.open(path, &block)
    self.new(path, &block)
  end

  def initialize(path, &block)
    @replace = {}
    if block_given?
      @zip = Zip::File.open(path)
      yield(self)
      @zip.close
    else
      @zip = Zip::File.open(path)
    end
  end

  def force_settings
    @replace["word/settings.xml"] = %{<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:settings xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:sl="http://schemas.openxmlformats.org/schemaLibrary/2006/main"><w:zoom w:percent="100"/></w:settings>}
  end

  def merge(rec)
    xml = @zip.read("word/document.xml")

    # replace tags with correct content

    @replace["word/document.xml"] = xml
  end

  def save(path)
    Zip::File.open(path, Zip::File::CREATE) do |out|
      @zip.each do |entry|

        if @replace[entry.name]
          # this line creates the error
          out.get_output_stream(entry.name).write(@replace[entry.name])
        else
          # this line also will do it.
          out.get_output_stream(entry.name).write(@zip.read(entry.name))
        end
      end
    end
  end

  def close
    @zip.close
  end
end

w = WordMailMerge.open("Option_2.docx")
w.force_settings
w.merge({})
w.save("Option_2_new.docx")

以下是堆栈跟踪:

/home/aaron/.rvm/rubies/ruby-2.4.1/lib/ruby/2.4.0/delegate.rb:85:in `call': cannot open entry for reading while its open for writing - [Content_Types].xml (StandardError)
    from /home/aaron/.rvm/rubies/ruby-2.4.1/lib/ruby/2.4.0/delegate.rb:85:in `method_missing'
    from /home/aaron/.rvm/gems/ruby-2.4.1@appt/gems/rubyzip-1.2.1/lib/zip/streamable_stream.rb:28:in `get_input_stream'
    from /home/aaron/.rvm/gems/ruby-2.4.1@appt/gems/rubyzip-1.2.1/lib/zip/streamable_stream.rb:45:in `write_to_zip_output_stream'
    from /home/aaron/.rvm/gems/ruby-2.4.1@appt/gems/rubyzip-1.2.1/lib/zip/file.rb:313:in `block (3 levels) in commit'
    from /home/aaron/.rvm/gems/ruby-2.4.1@appt/gems/rubyzip-1.2.1/lib/zip/entry_set.rb:38:in `block in each'
    from /home/aaron/.rvm/gems/ruby-2.4.1@appt/gems/rubyzip-1.2.1/lib/zip/entry_set.rb:37:in `each'
    from /home/aaron/.rvm/gems/ruby-2.4.1@appt/gems/rubyzip-1.2.1/lib/zip/entry_set.rb:37:in `each'
    from /home/aaron/.rvm/gems/ruby-2.4.1@appt/gems/rubyzip-1.2.1/lib/zip/file.rb:312:in `block (2 levels) in commit'
    from /home/aaron/.rvm/gems/ruby-2.4.1@appt/gems/rubyzip-1.2.1/lib/zip/output_stream.rb:53:in `open'
    from /home/aaron/.rvm/gems/ruby-2.4.1@appt/gems/rubyzip-1.2.1/lib/zip/file.rb:311:in `block in commit'
    from /home/aaron/.rvm/gems/ruby-2.4.1@appt/gems/rubyzip-1.2.1/lib/zip/file.rb:409:in `block in on_success_replace'
    from /home/aaron/.rvm/rubies/ruby-2.4.1/lib/ruby/2.4.0/tmpdir.rb:130:in `create'
    from /home/aaron/.rvm/gems/ruby-2.4.1@appt/gems/rubyzip-1.2.1/lib/zip/file.rb:407:in `on_success_replace'
    from /home/aaron/.rvm/gems/ruby-2.4.1@appt/gems/rubyzip-1.2.1/lib/zip/file.rb:310:in `commit'
    from /home/aaron/.rvm/gems/ruby-2.4.1@appt/gems/rubyzip-1.2.1/lib/zip/file.rb:334:in `close'
    from /home/aaron/.rvm/gems/ruby-2.4.1@appt/gems/rubyzip-1.2.1/lib/zip/file.rb:103:in `ensure in open'
    from /home/aaron/.rvm/gems/ruby-2.4.1@appt/gems/rubyzip-1.2.1/lib/zip/file.rb:103:in `open'
    from zip.rb:34:in `save'
    from zip.rb:56:in `<main>'

1 个答案:

答案 0 :(得分:1)

您需要将更新代码更改为

  def save(path)
    Zip::File.open(path, Zip::File::CREATE) do |out|
      @zip.each do |entry|

        if @replace[entry.name]
          # this line creates the error
          out.get_output_stream(entry.name){ |f| f.puts @replace[entry.name] }
        else
          # this line also will do it.
          # out.get_output_stream(entry.name).write(@zip.read(entry.name))
          out.get_output_stream(entry.name){ |f|  f.puts @zip.read(entry.name) }
        end
      end
    end
  end

然后文件将被创建

修改-1

以下是我用于测试的最终代码

require 'rubygems'
require 'zip' # rubyzip gem

class WordMailMerge
  def self.open(path, &block)
    self.new(path, &block)
  end

  def initialize(path, &block)
    @replace = {}
    if block_given?
      @zip = Zip::File.open(path)
      yield(self)
      @zip.close
    else
      @zip = Zip::File.open(path)
    end
  end

  def force_settings
    @replace["word/settings.xml"] = %{<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:settings xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:sl="http://schemas.openxmlformats.org/schemaLibrary/2006/main"><w:zoom w:percent="100"/></w:settings>}
  end

  def merge(rec)
    xml = @zip.read("word/document.xml")

    # replace tags with correct content

    @replace["word/document.xml"] = xml.gsub("{name}", "Tarun lalwani")
  end

  def save(path)
    Zip::File.open(path, Zip::File::CREATE) do |out|
      @zip.each do |entry|

        if @replace[entry.name]
          # this line creates the error
          out.get_output_stream(entry.name){ |f| f.puts @replace[entry.name] }
        else
          # this line also will do it.
          # out.get_output_stream(entry.name).write(@zip.read(entry.name))
          out.get_output_stream(entry.name){ |f|  f.puts @zip.read(entry.name) }
        end
      end
    end
  end

  def close
    @zip.close
  end
end

w = WordMailMerge.open("Option_2.docx")
w.force_settings
w.merge({})
w.save("Option_2_new.docx")

<强> Option_2.docx

Old doc

<强> Option_2_new.doc

Updated document