2013-08-20 1 views
3

Я кодирую небольшой веб-сайт с Python и CGI, где пользователи могут загружать zip-файлы и загружать файлы, загруженные другими пользователями. В настоящее время я могу правильно загрузить zip, но у меня возникли проблемы с правильной отправкой файлов пользователю. Мой первый подход:Как развернуть zip-файлы (или другие двоичные файлы) через cgi в Python?

file = open('../../data/code/' + filename + '.zip','rb') 

print("Content-type: application/octet-stream") 
print("Content-Disposition: filename=%s.zip" %(filename)) 
print(file.read()) 

file.close() 

Но вскоре я понял, что я должен был отправить файл в двоичном виде, поэтому я попытался:

print("Content-type: application/octet-stream") 
print("Content-Disposition: filename=%s.zip" %(filename)) 
print('Content-transfer-encoding: base64\r') 
print(base64.b64encode(file.read()).decode(encoding='UTF-8')) 

И разные варианты этого. Это просто не работает; Apache вызывает ошибку «неправильный заголовок из сценария», поэтому я предполагаю, что я должен закодировать файл каким-то другим способом.

ответ

2

Вы должны напечатать пустой строки после заголовков, и заголовок Content-диспозиции отсутствует тип (attachment):

print("Content-type: application/octet-stream") 
print("Content-Disposition: attachment; filename=%s.zip" %(filename)) 
print() 

Вы также можете использовать более эффективный метод загрузки результирующий файл; использовать shutil.copyfileobj(), чтобы скопировать данные в sys.stdout.buffer:

from shutil import copyfileobj 
import sys 

print("Content-type: application/octet-stream") 
print("Content-Disposition: attachment; filename=%s.zip" %(filename)) 
print() 

with open('../../data/code/' + filename + '.zip','rb') as zipfile: 
    copyfileobj(zipfile, sys.stdout.buffer) 

Вы не должны использовать print() для двоичных данных в любом случае; все, что вы получаете, это b'...' байтовый синтаксис. Объект sys.stdout.buffer является базовым двоичным буфером ввода-вывода, напрямую копируя двоичные данные.

+0

Я уже печатаю пустую строку. Обратите внимание на «\ r» в конце печати («Content-transfer-encoding: base64 \ r») и помните, что «print» автоматически добавляет «\ n» в конце. Я только что пробовал свой код, но Apache продолжает жаловаться на неправильный заголовок. Во всяком случае, это выглядит намного лучше. – derkomai

+0

Право; похоже, что ваш заголовок также искажен; см. [RFC 2616] (http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html). –

+0

Я не вижу, где заголовок неверен. Но я только что успешно протестировал, что, если я создам ссылку на файл, он автоматически отправляется без использования предыдущего кода. Каков правильный путь? – derkomai

2

Заголовок неправильный, поскольку по какой-то причине Python отправляет его после отправки файла.

Что вам нужно сделать, это флеш стандартный вывод сразу после заголовка:

sys.stdout.flush() 

Затем поместите копию файла

+1

Не уверен, что это хороший ответ, но может быть полезно объяснить немного больше. –

1

Это то, что работает для меня, я бегу Apache2 и загрузки этого сценария с помощью CGI , Python 3 - мой язык.

Возможно, вам придется заменить первую строку вашим пулом 3-битного пула python.

#!/usr/bin/python3 
import cgitb 
import cgi 
from zipfile import ZipFile 
import sys 

# Files to include in archive 
source_file = ["/tmp/file1.txt", "/tmp/file2.txt"] 

# Name and Path to our zip file. 
zip_name = "zipfiles.zip" 
zip_path = "/tmp/{}".format(zip_name) 

with ZipFile(zip_path,'w') as zipFile: 
    for f in source_file: 
     zipFile.write(f); 

# Closing File. 
zipFile.close() 

# Setting Proper Header. 
print ('Content-Type:application/octet-stream; name="{}"'.format(zip_name)); 
print ('Content-Disposition:attachment; filename="{}"\r\n'.format(zip_name)); 

# Flushing Out stdout. 
sys.stdout.flush() 

bstdout = open(sys.stdout.fileno(), 'wb', closefd=False) 
file = open(zip_path,'rb') 
bstdout.write(file.read()) 
bstdout.flush() 
Смежные вопросы