Bottle est le cadre d'application Web léger de Python. Les applications Web génèrent parfois des fichiers côté serveur et les téléchargent. Cette fois, j'écrirai sur l'implémentation du téléchargement de fichiers dans Bottle.
Dans les applications Web, je pense que vous créez souvent un répertoire qui collecte des fichiers images / CSS / JS tels que / static /
, mais dans de tels cas, vous ne pouvez pas créer de gestionnaires de requêtes un par un avec la fonction route
, donc Il est pratique d'utiliser la fonction «static_file» de «bouteille» comme suit.
Dans cet exemple, accéder à http: // localhost: 8080 / static / hoge.txt
fournira le contenu de hoge.txt
dans. / Static
comme vu depuis le répertoire courant au moment de l'exécution.
Dans le déploiement en production de nombreuses applications Web, je pense que le répertoire qui collecte les fichiers statiques est généralement livré directement depuis nginx ou apache. Cependant, si vous prenez en charge les applications Python écrites en bouteille, vous pourrez lire des fichiers statiques pendant le développement local. (Par rapport à notre entreprise)
# -*- coding: utf-8 -*-
from bottle import route, run, static_file
@route('/static/<file_path:path>')
def static(file_path):
return static_file(file_path, root='./static')
run(host='localhost', port=8080)
En regardant Bottle source, il semble que les principaux formats de fichiers soient déduits par le paquet mimetypes
par extension. est. Les fichiers communs tels que les images, les fichiers css et zip / gz sont susceptibles d'avoir le type MIME correct dans l'en-tête HTTP Content-Type
.
Définissons l'option download
de la fonction static_file
sur True
.
# -*- coding: utf-8 -*-
from bottle import route, run, static_file
@route('/static/<file_path:path>')
def static(file_path):
return static_file(file_path, root='./static', download=True)
run(host='localhost', port=8080)
Si vous souhaitez séparer le nom du fichier à lire et le nom du fichier à enregistrer par le navigateur, définissez le nom du fichier à enregistrer dans le navigateur dans l'option télécharger
. Les détails seront décrits plus loin dans la section "Arguments / options de la fonction Static_file".
Vous pouvez utiliser la fonction static_file
telle quelle.
# -*- coding: utf-8 -*-
from bottle import route, run, static_file
@route('/sample_image.png')
def sample_image():
return static_file('./my_sample_image.png', root='.')
run(host='localhost', port=8080)
S'il est implémenté avec HTTPResponse
, ce serait comme suit.
Cela semble utile lorsque vous devez éditer le contenu du fichier au moment de l'exécution et faire des choses délicates telles que la distribution.
Cependant, si vous souhaitez simplement télécharger un fichier existant, il est fondamentalement simple et fiable d'utiliser la fonction static_file
.
Vous pouvez utiliser la fonction static_file
pour prendre en charge des détails tels que l'estimation du type MIME et la création d'une réponse 404 lorsque le fichier n'existe pas.
# -*- coding: utf-8 -*-
from bottle import route, run, template, response
@route('/sample_image.png')
def sample_image():
response.content_type = 'image/png'
with open('./my_sample_image.png', 'rb') as fh:
content = fh.read()
response.set_header('Content-Length', str(len(content)))
return content
run(host='localhost', port=8080)
Maintenant, lorsque vous accédez à http: // localhost: 8080 / sample_image.png
, l'image sera affichée.
Le gestionnaire de la requête HTTP appelée à partir de Bottle utilise la propriété selon laquelle la valeur de retour du gestionnaire devient le corps de la réponse telle quelle. L'en-tête de la réponse HTTP est modifié en modifiant l'objet de réponse du package de bouteille tel quel. Personnellement, je préfère assembler et renvoyer explicitement un objet HTTPResponse comme celui ci-dessous.
# -*- coding: utf-8 -*-
from bottle import route, run, template, HTTPResponse
@route('/sample_image.png')
def sample_image():
with open('./my_sample_image.png', 'rb') as fh:
content = fh.read()
resp = HTTPResponse(status=200, body=content)
resp.content_type = 'image/png'
resp.set_header('Content-Length', str(len(content)))
return resp
run(host='localhost', port=8080)
Si la valeur de retour du gestionnaire est HTTPResponse, le gestionnaire de la requête HTTP appelée depuis Bottle renverra la réponse en fonction du contenu de l'objet HTTPResponse. Pour plus d'informations sur HTTPResponse, reportez-vous à Objet Requête / Réponse de Mastering Bottle.
Les fichiers volumineux qui ne tiennent pas dans la mémoire doivent être lus petit à petit à partir du fichier et écrits sur le socket.
Même dans de tels cas, static_file
peut être utilisé. static_file
n'est pas une réponse dans laquelle tout le contenu du fichier est lu, mais une implémentation qui lit et distribue petit à petit le fichier en interne.
Après enquête, il semble que lorsque le gestionnaire de bouteilles devient un générateur, chaque élément généré par le générateur est envoyé au client sous la forme d'un fragment fragmenté du corps. Même dans la [Source] de static_file
(https://github.com/bottlepy/bottle/blob/master/bottle.py#L2725), le contenu est lu petit à petit par la fonction de générateur _file_iter_range
et il devient la réponse. Et il semble.
Si vous l'implémentez vous-même, ce sera comme suit.
# -*- coding: utf-8 -*-
from bottle import route, run, template, response
@route('/sample_image.png')
def sample_image():
response.content_type = 'image/png'
bufsize = 1024 # 1KB
with open('./my_sample_image.png', 'rb') as fh:
while True:
buf = fh.read(bufsize)
if len(buf) == 0:
break # EOF
yield buf
run(host='localhost', port=8080)
Vous n'avez pas à vous souvenir de cette méthode car il y a un static_file
? Il y a aussi une histoire,
Je pense qu'il peut y avoir une implémentation telle que la lecture à partir de la base de données à la volée sans passer par un fichier, le traiter dans un format de fichier CSV et l'envoyer au client. En fait, j'ai trouvé comment le faire dans un tel cas, alors j'espère que cela aidera quelqu'un d'autre.
Si vous souhaitez implémenter l'objet HTTPResponse
sous la forme de return
, définissez le générateur sur le corps
de l'objet HTTPResponse
.
# -*- coding: utf-8 -*-
from bottle import route, run, template, HTTPResponse
@route('/sample_image.png')
def sample_image():
resp = HTTPResponse(status=200)
resp.content_type = 'image/png'
def _file_content_iterator():
bufsize = 1024 # 1KB
with open('./my_sample_image.png', 'rb') as fh:
while True:
buf = fh.read(bufsize)
if len(buf) == 0:
break # EOF
yield buf
resp.body = _file_content_iterator()
return resp
run(host='localhost', port=8080)
Cependant, cette méthode peut également être un peu désagréable car elle est difficile à comprendre à première vue, comme la définition d'une fonction à l'intérieur d'une fonction. Ceci est votre préféré.
L'implémentation du générateur peut être difficile à comprendre pour ceux qui ne connaissent pas Python, mais j'ai écrit un article sur les itérateurs / générateurs Python itérateurs et générateurs Python. Jetez un œil si vous le souhaitez.
Il s'agit plus de HTTP que de Bottle, mais vous pouvez le faire avec l'en-tête Content-Disposition
. Si vous souhaitez utiliser la fonction static_file
, vous pouvez définir l'option download
sur True
comme décrit ci-dessus. Ici, je présenterai l'implémentation de l'interaction en utilisant HTTPResponse
.
# -*- coding: utf-8 -*-
from bottle import route, run, template, response
@route('/sample_image.png')
def sample_image():
response.content_type = 'image/png'
with open('./my_sample_image.png', 'rb') as fh:
content = fh.read()
response.set_header('Content-Length', str(len(content)))
download_fname = 'hoge.png'
response.set_header('Content-Disposition', 'attachment; filename="%s"' % download_fname.encode('utf-8'))
return content
run(host='localhost', port=8080)
Maintenant, lorsque vous accédez à http: // localhost: 8080 / sample_image.png
, l'image sera enregistrée avec le nom de fichier hoge.png
.
La signature de la fonction static_file
est la suivante.
def static_file(filename, root,
mimetype='auto',
download=False,
charset='UTF-8'):
....
--filename
: Le nom du fichier dans root
qui contient le contenu que vous voulez livrer.
--root
: Le répertoire contenant le fichier filename
qui contient le contenu que vous voulez livrer.
-- type mime
: facultatif. Par défaut, il est automatiquement deviné par le package mimetypes
.
--download
: facultatif. Si vous souhaitez télécharger sous la forme d'une boîte de dialogue d'enregistrement ou enregistrer automatiquement dans le dossier de téléchargement, spécifiez True
ou le nom lors de l'enregistrement côté client sous forme de chaîne de caractères. «False» pour l'affichage en ligne. La valeur par défaut est «False»
-- charset
: facultatif. Si mimetype commence par text /
ou est ʻapplication / javascript,
; charset = (value of charset) ʻest ajouté à l'en-tête HTTP Content-Type
.
Recommended Posts