write a file multiple times - ruby

I would like to know how to write a picture twice without writing it once and then copying it.
When a picture is downloaded, it is written in the /tmp and then copied to the wanted path ( I think ) meaning that the following code :
cover_buffer = download_pic(link)
buffer2 = cover_buffer
open(#dir + 'cover.jpg', 'wb') do |pic|
pic << cover_buffer.read()
end
open(#dir + 'cover2.jpg', 'wb') do |pic|
pic << cover_buffer2.read()
end
does not work since the both cover_buffer and buffer2 both point to the same file whitch was moved when writing cover.jpg
Executing that code will correctly write the picture in cover.jpg but cover2.jpg will be an empty file

TESTED SOLUTION
In file two_for_one.rb:
current_dir = File.expand_path(File.dirname(__FILE__))
new_file_1 = File.new(File.join(current_dir, 'image_1.png'), 'w')
new_file_2 = File.new(File.join(current_dir, 'image_2.png'), 'w')
origin_file = File.join(current_dir, 'original_image.png')
begin
File.open(origin_file, "r") do |source|
until source.eof?
chunk = source.read(1024)
new_file_1.write(chunk)
new_file_2.write(chunk)
end
end
ensure
new_file_1.close()
new_file_2.close()
end
Command line:
$ ruby two_for_one.rb

Related

Ruby: how to read an mp4 file into chunks

I want to be able to read an mp4 file in chunks of 1mb.
I've tried opening the file with the following API's:
video_file = File.open(#video_filename, 'rb')
video_file = IO.binread(#video_filename)
The problem is, video_file is a string afterwards and I cannot use read to get chunks of the file.
chunk = video_file.read(4*1024*1024)
What is the right interface/tools to use in Ruby to open this file, and read it for N bytes at a time?
I suppose I would do:
chnk_size=4*1024*1024
f=File.open(fn, 'rb')
until f.eof?
chnk=f.read(chnk_size)
# process the chnk
end
Try something like this :
`FILENAME = "d:\\tmp\\file.bin"
MEGABYTE = 1024 * 1024
class File
def each_chunk(chunk_size = MEGABYTE)
yield read(chunk_size) until eof?
end
end
open(FILENAME, "rb") do |f|
f.each_chunk { |chunk| puts chunk }
end`

ruby write all lines of puts in file?

I have my file
ppp.txt
mmm;2;nsfnjd;pet;
sadjjasjnsd;6;gdhjsd;pet;
gsduhdssdj;3;gsdhjhjsd;dog;
I need to write
nsfnjd
gsdhjhjsd
I use this code but only print the last line "gsdhjhjsd"
I dont know what is doing wrong
File.open("ppp.txt", "r") do |fi|
fi.readlines.each do |line|
parts = line.chomp.split(';')
if parts[1].to_i < 4
puts parts[2]
File.open("testxx.txt", "w+") do |f|
f. puts parts[2]
end
end
end
end
Please help me
Open the file using append mode, 'a+' instead of write mode 'w+', which overwrites the file, as the open command is called inside a loop.
Or open the write file prior to looping the lines of the read file.
open the file descriptor outside the loop
fo = File.open("testxx.txt","w+")
File.open("ppp.txt", "r") do |fi|
fi.readlines.each do |line|
parts = line.chomp.split(';')
fo.puts parts[2] if parts[1].to_i < 4
end
end
fo.close()
NOTE: Need to explicitly close fo, but file open with block; ruby close the file automatically (fi case).

Ruby (Errno::EACCES) on File.delete - previous solution doesn't work

I am having exactly the same issue as the poster of this question : Ruby (Errno::EACCES) on File.delete. Unlike him, the change provided in the solution for him does not work for me.
Here is my code, it is a compression algorithm where I want to delete the original file :
uncompressed_file = File.new(Rails.root + filepath)
compressed_file = File.new(Rails.root + "#{filepath[0..filepath.size - 1]}.gz", "w+b")
file_writer = Zlib::GzipWriter.new(compressed_file)
buf = ""
File.open(uncompressed_file, "rb") do | uncompressed |
while uncompressed.read(4096, buf)
file_writer << buf
end
file_writer.close
end
begin
files_changed_by_chmod = File.chmod(0777, uncompressed_file)
rescue
puts "Something happened"
end
puts "Number of files changed by CHMOD : " + files_changed_by_chmod.to_s
File.delete(uncompressed_file)
File.rename(Rails.root + "#{filepath[0..filepath.size - 1]}.gz", Rails.root + filepath)
You'll notice there are a couple puts in there to confirm what is happening with the chmod. The output is this :
Number of files changed by CHMOD : 1
and there is no Something happened. Therefore there is no error generated by running the chmod, and chmod indeed modifies one file (presumably, the uncompressed_file.) However, I still get the Errno::EACCESS error on the delete line.
Why can't I delete the files?! It's driving me up the wall. I'm running Windows 8 and ruby 1.9.3.
EDIT: The first answer below solves the issue of not being able to delete the files; however, it invalidates the job my code is trying to do (i.e, when my files are run through the compression algorithm supplied in the solution and then my other algorithms, the file comes back corrupted). Yes, I did also try to emulate the coding style here in my inflation method, but that didn't help. Here is the rest of the code that performs the encryption, decryption, and decompression of my files :
def inflate_attachment(filepath)
compressed_file = File.new(Rails.root + filepath)
File.open(compressed_file, "rb") do | compressed |
File.open(Rails.root + "#{filepath[0..filepath.size - 7]}_FULL.enc", 'w+b') do | decompressed |
gz = Zlib::GzipReader.new(compressed)
result = gz.read
decompressed.write(result)
gz.close
end
end
end
def encrypt_attachment(filepath, cipher)
unencrypted_file = File.new(Rails.root + filepath)
encrypted_file = File.new(Rails.root + "#{filepath[0..filepath.size - 1]}.enc", "w")
buf = ""
File.open(encrypted_file, "wb") do |outf|
File.open(unencrypted_file, "rb") do |inf|
while inf.read(4096, buf)
outf << cipher.update(buf)
end
outf << cipher.final
end
end
end
def decrypt_attachment(filepath, key, iv)
cipher = OpenSSL::Cipher.new(ENCRYPTION_TYPE)
cipher.decrypt
cipher.key = key
cipher.iv = iv
encrypted_file = File.new(Rails.root + filepath)
decrypted_file = File.new(Rails.root + "#{filepath[0..filepath.size - 5]}.dec", "w")
buf = ""
File.open(decrypted_file, "wb") do |outf|
File.open(encrypted_file, "rb") do |inf|
while inf.read(4096, buf)
outf << cipher.update(buf)
end
outf << cipher.final
end
end
end
I think that might have something to do that you haven't properly closed the files. I took the liberty to rewrite your code, without the chmod stuff (which I don't think is necessary)
filename = <your sourcefilename goes here>
filename_gz = filename + ".gz"
filepath = Rails.root + filename
filepath_gz = Rails.root + filename_gz
# gzip the file
buffer = ""
File.open(filepath) do |file|
Zlib::GzipWriter.open(filepath_gz) do |gz|
while file.read(4096, buffer)
gz << buffer
end
end
end
# moves the filepath_gz to filepath (overwriting the original file in the process!)
FileUtils.mv(filepath_gz, filepath)
As you can see I've used File.open(path) and passed a block. This has the effect that the files will be closed automatically when the block exits.
I've also changed the delete/rename code to simply move the gziped file to the original path, which has the same effect.
However, I strongly advice you to keep a backup of your original file.

In Ruby- Parsing Directory and reading first row of the file

Below is the piece of code that is supposed read the directory and for each file entry prints the first row of the file. The issue is x is not visible so file is not being parsed.
Dir.foreach("C:/fileload/src") do |file_name|
x = file_name
puts x
f = File.open("C:/fileload/src/" +x)
f.readlines[1..1].each do |line|
puts line
end
end
Why are you assigning x to file_name? You can use file_name directly. And if you are only reading the first line of the file, why not try this?
#!/usr/bin/ruby
dir = "C:/fileload/src"
Dir.foreach(dir) do |file_name|
full = File.join(dir, file_name)
if File.file?(full)
f = File.open(full)
puts f.first
f.close
end
end
You should use File.join to safely combine paths in Ruby. I also checked that you are opening a file using the File.file? method.
You have no visibility issue with x. You should be using File::join or Pathname#+ to build your file paths. You should exclude non-files from consideration. You're selecting the second line, not the first with [1..1]. Here's a cleaner correct replacement for your sample code.
dir = "C:/fileload/src"
Dir.foreach(dir).
map { |fn| File.join(dir,fn) }.
select { |fn| File.file?(fn) }.
each { |fn| puts File.readlines(fn).first }

How can I copy the contents of one file to another using Ruby's file methods?

I want to copy the contents of one file to another using Ruby's file methods.
How can I do it using a simple Ruby program using file methods?
There is a very handy method for this - the IO#copy_stream method - see the output of ri copy_stream
Example usage:
File.open('src.txt') do |f|
f.puts 'Some text'
end
IO.copy_stream('src.txt', 'dest.txt')
For those that are interested, here's a variation of the IO#copy_stream, File#open + block answer(s) (written against ruby 2.2.x, 3 years too late).
copy = Tempfile.new
File.open(file, 'rb') do |input_stream|
File.open(copy, 'wb') do |output_stream|
IO.copy_stream(input_stream, output_stream)
end
end
As a precaution I would recommend using buffer unless you can guarantee whole file always fits into memory:
File.open("source", "rb") do |input|
File.open("target", "wb") do |output|
while buff = input.read(4096)
output.write(buff)
end
end
end
Here my implementation
class File
def self.copy(source, target)
File.open(source, 'rb') do |infile|
File.open(target, 'wb') do |outfile2|
while buffer = infile.read(4096)
outfile2 << buffer
end
end
end
end
end
Usage:
File.copy sourcepath, targetpath
Here is a simple way of doing that using ruby file operation methods :
source_file, destination_file = ARGV
script = $0
input = File.open(source_file)
data_to_copy = input.read() # gather the data using read() method
puts "The source file is #{data_to_copy.length} bytes long"
output = File.open(destination_file, 'w')
output.write(data_to_copy) # write up the data using write() method
puts "File has been copied"
output.close()
input.close()
You can also use File.exists? to check if the file exists or not. This would return a boolean true if it does!!
Here's a fast and concise way to do it.
# Open first file, read it, store it, then close it
input = File.open(ARGV[0]) {|f| f.read() }
# Open second file, write to it, then close it
output = File.open(ARGV[1], 'w') {|f| f.write(input) }
An example for running this would be.
$ ruby this_script.rb from_file.txt to_file.txt
This runs this_script.rb and takes in two arguments through the command-line. The first one in our case is from_file.txt (text being copied from) and the second argument second_file.txt (text being copied to).
You can also use File.binread and File.binwrite if you wish to hold onto the file contents for a bit. (Other answers use an instant copy_stream instead.)
If the contents are other than plain text files, such as images, using basic File.read and File.write won't work.
temp_image = Tempfile.new('image.jpg')
actual_img = IO.binread('image.jpg')
IO.binwrite(temp_image, actual_img)
Source: binread,
binwrite.

Resources