Sending a file via http

Hello,

I’m trying to send a file using http in a ruby script of mine. I’ve been
trying to find an answer for this, but haven’t been satisfied with anything
I’ve seen so far.

What I’m doing is this…
I’ve got a file on my host machine (no direct link to it from the web
page–and I don’t want one) and the user is clicking on a link invoking my
ruby script which in turn attempts to send the file to the client.

This is what I have so far…

file = File.open(“data.zip”)
puts "Content-type: application/x-zip-compressed\n"
puts “Content-length: “+File.size(“album.zip”).to_s+”\n\n”

ok, now what? How do I send the file data? I can send plain text no
problem, but I don’t know how to efficiently send this file. Any help would
be appreciated.

Thanks,
Andrew

I’m trying to send a file using http in a ruby script of mine.

A CGI, presumably?

This is what I have so far…

file = File.open(“data.zip”)
puts “Content-type: application/x-zip-compressed\n”
puts “Content-length: “+File.size(“album.zip”).to_s+”\n\n”

ok, now what? How do I send the file data?

You should be OK with:

print File.read(“album.zip”)

Or slightly more efficient,

File.open(“album.zip”) do |f|
print f.read
end

because it closes the file immediately, rather than waiting until
garbage-collection time.

If running as a CGI you shouldn’t need to generate a Content-length: header,
just let your webserver deal with it. It can use chunked transfer encoding
if the file is large.

Regards,

Brian.

···

On Tue, Apr 08, 2003 at 02:32:35PM +0900, Andrew wrote:

Yes, this is using CGI. Your code seems to do almost what I want, but it
reads a few (10-20?) bytes and then stops. Is there something in the zip
file that ‘read’ would interpret as an end of file maybe and cause it to
stop?

Thanks,
Andrew

“Brian Candler” B.Candler@pobox.com wrote in message
news:20030408080007.GA17485@uk.tiscali.com

I’m trying to send a file using http in a ruby script of mine.

A CGI, presumably?

This is what I have so far…

file = File.open(“data.zip”)
puts “Content-type: application/x-zip-compressed\n”
puts “Content-length: “+File.size(“album.zip”).to_s+”\n\n”

ok, now what? How do I send the file data?

You should be OK with:

print File.read(“album.zip”)

Or slightly more efficient,

File.open(“album.zip”) do |f|
print f.read
end

because it closes the file immediately, rather than waiting until
garbage-collection time.

If running as a CGI you shouldn’t need to generate a Content-length:
header,

···

On Tue, Apr 08, 2003 at 02:32:35PM +0900, Andrew wrote:
just let your webserver deal with it. It can use chunked transfer encoding
if the file is large.

Regards,

Brian.

Yes, this is using CGI. Your code seems to do almost what I want, but it
reads a few (10-20?) bytes and then stops. Is there something in the zip
file that ‘read’ would interpret as an end of file maybe and cause it to
stop?

Oops, my fault:

a = File.read(“/etc/passwd”)
NameError: undefined method `read’ for File:Class

Try:

File.open(“album.zip”).read

If your CGI generates errors like that, you should see them in your
webserver’s error log (e.g. /var/log/apache/logs/error_log or something like
that)

Cheers,

Brian.

···

On Wed, Apr 09, 2003 at 08:37:02AM +0900, Andrew wrote:

Brian Candler wrote:

a = File.read(“/etc/passwd”)
NameError: undefined method `read’ for File:Class

Try:

File.open(“album.zip”).read

Which reminds me again of the handy little utility methods that I’ve
added to class File (see below).

Are these methods useful enough that we should consider adding them to
the standard Ruby File class, or should I be content with requiring them

class File
# Reads the contents of the specified file_name and returns them as
# a String.
def File.read(file_name)
File.open(file_name) { |f| f.read }
end

 # Writes +content+ to the file with the specified +file_name+,
 # overwriting any existing content of the file.
 def File.write(file_name, content)
     File.open(file_name, 'w') { |f| f.write(content) }
 end

 # Writes +content+ to the file with the specified +file_name+,
 # appending to any existing content of the file.
 def File.append(file_name, content)
     File.open(file_name, 'a') { |f| f.write(content) }
 end

 # Reads the contents of the file with the specified +file_name+ and
 # passes the contents to the provided block.  The contents of the
 # file are then replaced by whatever is returned from the block.
 def File.filter(file_name)
     File.write(file_name, yield(File.read(file_name)))
 end

end

···

from my ‘tools’ library? matz, your thoughts?


Jason Voegele
“There is an essential core at the center of each man and woman that
remains unaltered no matter how life’s externals may be transformed
or recombined. But it’s smaller than we think.”
– Gene Wolfe, The Book of the Long Sun

I’m not getting any errors. The problem is that the whole file isn’t being
read. It reads some and outputs what it reads, but it’s far from reading
everything. I have a 30MB file and it’s only reading a handful of bytes and
then stops (no errors). I want it to read the whole file, not part of it.
This is a binary file that I’m attempting to read, not text, so would that
cause any problems?

Andrew

“Brian Candler” B.Candler@pobox.com wrote in message
news:20030409093428.A30588@linnet.org

Yes, this is using CGI. Your code seems to do almost what I want, but
it
reads a few (10-20?) bytes and then stops. Is there something in the
zip
file that ‘read’ would interpret as an end of file maybe and cause it to
stop?

Oops, my fault:

a = File.read(“/etc/passwd”)
NameError: undefined method `read’ for File:Class

Try:

File.open(“album.zip”).read

If your CGI generates errors like that, you should see them in your
webserver’s error log (e.g. /var/log/apache/logs/error_log or something
like

···

On Wed, Apr 09, 2003 at 08:37:02AM +0900, Andrew wrote:
that)

Cheers,

Brian.

I agree, except it might be an idea to add mode as an optional parameter:

  def File.read(file_name, mode='r')
      File.open(file_name, mode) { |f| f.read }
  end

  def File.write(file_name, content, mode='w')
      File.open(file_name, mode) { |f| f.write(content) }
  end

This is for the poor souls in Windows-land who need mode=‘rb’ or whatever to
stop their O/S mangling the file. Also eliminates the need for a separate
append method.

As for this one:

  def File.filter(file_name, readmode='r', writemode='w')
      File.write(file_name, yield(File.read(file_name, readmode)), writemode)
  end

If we have this then perhaps there should be a line-by-line filter as well -
File.sed ?? But it would have to write a temporary file.

Regards,

Brian.

···

On Wed, Apr 09, 2003 at 10:00:49PM +0900, Jason Voegele wrote:

Are these methods useful enough that we should consider adding them to
the standard Ruby File class, or should I be content with requiring them
from my ‘tools’ library?

Please describe how you know that it is outputting only a few bytes of your
file.

Can you try the following four-liner:

#!/usr/local/bin/ruby
$stdout.binmode
print “Content-Type: application/octet-stream\r\n\r\n”
f = File.open(“/path/to/album.zip”).read
print f

and if that doesn’t work then we can try to narrow it down from there.

Cheers,

Brian.

···

On Thu, Apr 10, 2003 at 10:22:13AM +0900, Andrew wrote:

I’m not getting any errors. The problem is that the whole file isn’t being
read. It reads some and outputs what it reads, but it’s far from reading
everything. I have a 30MB file and it’s only reading a handful of bytes and
then stops (no errors). I want it to read the whole file, not part of it.

Ok, I removed the content-type stuff as I’m doing this from the command line
for now first…

$stdout.binmode
f = File.open(“data.zip”).read
print f

output:
PK??
Eªq.ï§)²$ )²$

This file is just over 31MB, so that’s definitely not all of it.

My OS is windows if that matters.

“Brian Candler” B.Candler@pobox.com wrote in message
news:20030410083103.GA19771@uk.tiscali.com

I’m not getting any errors. The problem is that the whole file isn’t
being
read. It reads some and outputs what it reads, but it’s far from
reading
everything. I have a 30MB file and it’s only reading a handful of bytes
and
then stops (no errors). I want it to read the whole file, not part of
it.

Please describe how you know that it is outputting only a few bytes of
your

···

On Thu, Apr 10, 2003 at 10:22:13AM +0900, Andrew wrote:
file.

Can you try the following four-liner:

#!/usr/local/bin/ruby
$stdout.binmode
print “Content-Type: application/octet-stream\r\n\r\n”
f = File.open(“/path/to/album.zip”).read
print f

and if that doesn’t work then we can try to narrow it down from there.

Cheers,

Brian.

Andrew wrote:

This file is just over 31MB, so that’s definitely not all of it.

My OS is windows if that matters.

In fact it does. Make sure you open the file in binary mode:

f = File.open(“data.zip”, “rb”) {|f| f.read}

···


Jason Voegele
“We believe that we invent symbols. The truth is that they invent us.”
– Gene Wolfe, The Book of the New Sun