Pythonda cgi orqali zip fayllarni (yoki boshqa ikkilik fayllarni) qanday joylashtirish mumkin?

Men Python va CGI bilan kichik veb-saytni kodlayapman, u erda foydalanuvchilar zip fayllarni yuklashlari va boshqa foydalanuvchilar tomonidan yuklangan fayllarni yuklab olishlari mumkin. Hozirda men zip-ni to'g'ri yuklay olaman, lekin foydalanuvchiga fayllarni to'g'ri yuborishda muammolarga duch keldim. Mening birinchi yondashuvim:

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()

Ammo tez orada men faylni ikkilik sifatida yuborishim kerakligini angladim, shuning uchun men harakat qildim:

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') )

Va uning turli xil variantlari. Bu shunchaki ishlamaydi; Apache "skriptdan noto'g'ri shakllangan sarlavha" xatosini ko'taradi, shuning uchun faylni boshqa yo'l bilan kodlashim kerak deb o'ylayman.


person dvilela    schedule 20.08.2013    source manba


Javoblar (3)


Sarlavhalardan keyin boʻsh qatorni chop etishingiz kerak va Content-disposition sarlavhasida (attachment) turi yoʻq:

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

Olingan faylni yuklashning yanada samarali usulidan ham foydalanishni xohlashingiz mumkin; ma'lumotlarni sys.stdout.buffer ga nusxalash uchun shutil.copyfileobj() dan foydalaning:

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)

Hech qanday holatda ikkilik ma'lumotlar uchun print() dan foydalanmaslik kerak; faqat b'...' bayt literal sintaksisini olasiz. sys.stdout.buffer ob'ekti asosiy ikkilik kiritish-chiqarish buferidir, ikkilik ma'lumotlarni to'g'ridan-to'g'ri unga nusxalash.

person Martijn Pieters    schedule 20.08.2013
comment
Men Python 2.7 dan foydalanmoqdaman, bu farq qilishi mumkin, lekin bu kodni ishlashi uchun men oxirgi qatorni o'zgartirishim kerak edi copyfileobj(zipfile, sys.stdout). - person Daniel Griscom; 19.07.2015
comment
@DanielGriscom: ha, bu butunlay to'g'ri; Mening javobim Python 3 uchun yozilgan bo'lib, unda io kutubxonasi barcha kiritish-chiqarish bilan shug'ullanadi va sys.stdout.buffer asosiy io.BufferedIOBase tatbiq, (kodlash) io.TextIOBase; Python 2 da sys.stdout fayl obyekti bevosita kodlangan baytlarni qabul qiladi. - person Martijn Pieters; 20.07.2015
comment
Python 2 da ishlagan holda, men uchinchi qatordagi print() ni olib tashlashim kerak edi, shuningdek, yuqorida @DanielGriscom taklif qilganidek, oxirgi qatorni copyfileobj(zipfile, sys.stdout) ga o'zgartirishim kerak edi. - person eeScott; 19.01.2020
comment
@eeScott ha, chunki bu savol maxsus python-3.x bilan belgilangan. Python 2 da sys.stdout oddiy Python 2 fayl obyekti, io.TextIOWrapper() obyekti emas. - person Martijn Pieters; 19.01.2020
comment
Rozi bo'ldim, men buni keyingi odam uchun hujjatlashtirmoqchi edim, chunki bu savolga python 2 ga moslashtirilgan odamlar unchalik ko'p emas... - person eeScott; 19.01.2020

Sarlavha noto'g'ri tuzilgan, chunki ba'zi sabablarga ko'ra Python faylni yuborganidan keyin uni yuboradi.

Nima qilishingiz kerak bo'lsa, sarlavhadan keyin darhol stdout-ni o'chiring:

sys.stdout.flush()

Keyin fayl nusxasini qo'ying

person Alecz    schedule 16.06.2014
comment
Bu yaxshi javob ekanligiga ishonchim komil emas, lekin biroz ko'proq tushuntirish yaxshi bo'lishi mumkin. - person Aaron Hall; 16.06.2014

Bu men uchun ishlagan narsa, men Apache2 ni ishga tushiryapman va ushbu skriptni cgi orqali yuklayapman. Python 3 mening tilim.

Birinchi qatorni python 3 bin yo'li bilan almashtirishingiz kerak bo'lishi mumkin.

#!/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()
person Community    schedule 21.11.2016