2

So, I'm using Nokogiri and Rubyzip to unzip a .docx file, modify the word/docoument.xml file in it (in this case just change every element wrapped in to say "Dreams!"), and then zip it back up.

require 'nokogiri'
require 'zip'

zip = Zip::File.open("apple.docx")
doc = zip.find_entry("word/document.xml")

xml = Nokogiri::XML.parse(doc.get_input_stream)

inputs = xml.root.xpath("//w:t")

inputs.each{|element| element.content = "DREAMS!"}

zip.get_output_stream("word/document.xml", "w") {|f| f.write(xml.to_s)}

zip.close

Running the code through IRB line by line works perfectly and makes the changes to the .docx file as I needed, but if I run the script from the command line

ruby xmltodoc.rb   

I receive the following error:

C:/Ruby193/lib/ruby/gems/1.9.1/gems/rubyzip-1.1.7/lib/zip/file.rb:416:in `rename': Permission denied - (C:/Users/Bane/De
sktop/apple.docx20150326-6016-k9ff1n, apple.docx) (Errno::EACCES)
        from C:/Ruby193/lib/ruby/gems/1.9.1/gems/rubyzip-1.1.7/lib/zip/file.rb:416:in `on_success_replace'
        from C:/Ruby193/lib/ruby/gems/1.9.1/gems/rubyzip-1.1.7/lib/zip/file.rb:308:in `commit'
        from C:/Ruby193/lib/ruby/gems/1.9.1/gems/rubyzip-1.1.7/lib/zip/file.rb:332:in `close'
        from ./xmltodoc.rb:15:in `<main>' 

All users on my computer have all permissions for that .docx file. The file also doesn't have any special settings--just a new file with a paragraph. This error only shows up on Windows, but the script works perfectly on Mac and Ubuntu. Running Powershell as Admin throws the same error. Any ideas?

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Brewster
  • 195
  • 1
  • 1
  • 9
  • Welcome to Stack Overflow. Why would this be a Nokogiri problem? It has nothing to do with the permissions when reading or writing files. I'm removing the tag. To debug your code, remove all references to Nokogiri, then simply open then save the file and see if the problem continues. – the Tin Man Mar 30 '15 at 20:06

1 Answers1

1

On my Windows 7 system the following works.

require 'nokogiri'
require 'zip'

Zip::File.open("#{File.dirname(__FILE__)}/apple.docx") do |zipfile|
  doc = zipfile.read("word/document.xml")
  xml = Nokogiri::XML.parse(doc)
  inputs = xml.root.xpath("//w:t")
  inputs.each{|element| element.content = "DREAMS!"}
  zipfile.get_output_stream("word/document.xml") {|f| f.write(xml.to_s)}
end

Instead you also could use the gem docx, here is an example, the names of the bookmarks are in dutch because, well that's the language my MS Office is in.

require 'docx'

# Create a Docx::Document object for our existing docx file
doc = Docx::Document.open('C:\Users\Gebruiker\test.docx'.gsub(/\\/,'/'))

# Insert a single line of text after one of our bookmarks
# p doc.bookmarks['bladwijzer1'].methods
doc.bookmarks['bladwijzer1'].insert_text_after("Hello world.")

# Insert multiple lines of text at our bookmark
doc.bookmarks['bladwijzer3'].insert_multiple_lines(['Hello', 'World', 'foo'])

# Save document to specified path
doc.save('example-edited.docx')
peter
  • 41,770
  • 5
  • 64
  • 108
  • This worked perfectly! Thank you! But I'm curious, do you know why my code worked on Mac and Ubuntu but wouldn't work on Windows? I figured that there was a problem with 'zip.close' but doesn't Zip::File.open have an automatic .close once it finishes its block? And why would my code work from IRB but not when calling the file from the command line? Anyways, Thank you! – Brewster Mar 27 '15 at 14:02
  • 1
    i suppose it is a timing problem that doesn't occur in IRB, the temporary file is renamed tot the zip while the zip is still there, anyway it is better to use a bloc that opens ans closes a file automaticly, I should try to do that with my docx example too – peter Mar 27 '15 at 14:17