Cheok Yan Cheng
Cheok Yan Cheng

Reputation: 42758

Serving binary file from web server to client

Usually, when I want to transfer a web server text file to client, here is what I did

import cgi

print "Content-Type: text/plain"
print "Content-Disposition: attachment; filename=TEST.txt"
print

filename = "C:\\TEST.TXT"
f = open(filename, 'r')
for line in f:
    print line

Works very fine for ANSI file. However, say, I have a binary file a.exe (This file is in web server secret path, and user shall not have direct access to that directory path). I wish to use the similar method to transfer. How I can do so?

I use the following code.

#!c:/Python27/python.exe -u

import cgi

print "Content-Type: application/octet-stream"
print "Content-Disposition: attachment; filename=jstock.exe"
print

filename = "C:\\jstock.exe"
f = open(filename, 'rb')
for line in f:
    print line

However, when I compare the downloaded file with original file, it seems there is an extra whitespace (or more) for after every single line.

alt text

Upvotes: 4

Views: 7995

Answers (4)

dingles
dingles

Reputation: 1768

For anyone using Windows Server 2008 or 2012 and Python 3, here's an update...

After many hours of experimentation I have found the following to work reliably:

import io

with io.open(sys.stdout.fileno(),"wb") as fout:
    with open(filename,"rb") as fin:
        while True:
            data = fin.read(4096)
            fout.write(data)
            if not data:
                break

Upvotes: 0

Knio
Knio

Reputation: 7120

Agree with the above posters about 'rb' and Content-Type headers.

Additionally:

for line in f:
    print line

This might be a problem when encountering \n or \r\n bytes in the binary file. It might be better to do something like this:

import sys
while True:
    data = f.read(4096)
    sys.stdout.write(data)
    if not data:
        break

Assuming this is running on windows in a CGI environment, you will want to start the python process with the -u argument, this will ensure stdout isn't in text-mode

Upvotes: 3

systempuntoout
systempuntoout

Reputation: 74114

Content-type of .exe is tipically application/octet-stream.
You might want to read your file using open(filename, 'rb') where b means binary.

To avoid the whitespace problem, you could try with:

sys.stdout.write(open(filename,"rb").read())
sys.stdout.flush()

or even better, depending on the size of your file, use the Knio approach:

fo = open(filename, "rb")
while True:
    buffer = fo.read(4096)
    if buffer:
        sys.stdout.write(buffer)
    else:
        break
fo.close()

Upvotes: 1

Chris Morgan
Chris Morgan

Reputation: 90832

When opening a file, you can use open(filename, 'rb') - the 'b' flag marks it as binary. For a general handler, you could use some form of mime magic (I'm not familiar with using it from Python, I've only ever used it from PHP a couple of years ago). For the specific case, .exe is application/octet-stream.

Upvotes: 2

Related Questions