它看起來像問題可能是在這一行:
File.open(file, "rb") { |f| tf.write f.read }
通過執行f.read
來「輸入」輸入文件。 slurping意味着整個文件正在被讀入內存,這是不可擴展的,並且是使用read
而沒有長度的結果。
相反,我會做一些讀取和寫入塊的文件,所以你有一個一致的內存使用情況。這讀入1MB塊。您可以調整自己的需要:
BLOCKSIZE_TO_READ = 1024 * 1000
File.open(file, "rb") do |fi|
while buffer = fi.read(BLOCKSIZE_TO_READ)
tf.write buffer
end
end
這裏是the documentation說,關於read
:
If length is a positive integer, it try to read length bytes without any conversion (binary mode). It returns nil or a string whose length is 1 to length bytes. nil means it met EOF at beginning. The 1 to length-1 bytes string means it met EOF after reading the result. The length bytes string means it doesn’t meet EOF. The resulted string is always ASCII-8BIT encoding.
另外一個問題是,它看起來像你沒有正確地打開輸出文件:
tarfile = File.open("#{Pathname.new(path).realpath.to_s}.tar","w")
由於"w"
因爲您正在以「文本」模式寫入它。相反,你需要在二進制模式,"wb"
寫,因爲壓縮包包含二進制(壓縮)數據:
tarfile = File.open("#{Pathname.new(path).realpath.to_s}.tar","wb")
改寫原代碼更像我希望看到它,結果:
BLOCKSIZE_TO_READ = 1024 * 1000
def create_tarball(path)
tar_filename = Pathname.new(path).realpath.to_path + '.tar'
File.open(tar_filename, 'wb') do |tarfile|
Gem::Package::TarWriter.new(tarfile) do |tar|
Dir[File.join(path, '**/*')].each do |file|
mode = File.stat(file).mode
relative_file = file.sub(/^#{ Regexp.escape(path) }\/?/, '')
if File.directory?(file)
tar.mkdir(relative_file, mode)
else
tar.add_file(relative_file, mode) do |tf|
File.open(file, 'rb') do |f|
while buffer = f.read(BLOCKSIZE_TO_READ)
tf.write buffer
end
end
end
end
end
end
end
tar_filename
end
BLOCKSIZE_TO_READ
應該在你的文件的頂部,因爲它是一個常數,是一個「tweakable」 - 這是更有可能比代碼的身體改變。
該方法返回到tarball的路徑,而不是像原始代碼那樣的IO句柄。使用IO.open
的塊形式自動關閉輸出,這將導致任何後續的open
自動rewind
。我更喜歡繞過路徑字符串而不是IO處理文件。
我還在封閉括號中包裝了一些方法參數。儘管Ruby中的方法參數不需要括號,並且有些人避開了它們,但我認爲他們通過定義參數開始和結束的位置來使代碼更易於維護。當你將參數和塊傳遞給方法時,它們也可以避免混淆Ruby - 一個衆所周知的錯誤原因。
是否有理由不能執行對系統tar命令的調用? – mcfinnigan
是的,應用程序必須是可移植的。 – zulq
你使用的是什麼ruby環境?如果是JRuby,則可以通過-J-Xmx啓動選項增加可用於JVM的虛擬內存量;詳細在這裏:http://stackoverflow.com/questions/6910429/how-do-i-specify-heap-size-configuration-in-a-config-file – mcfinnigan