Quando o Django lida com o upload de arquivos, o arquivo de dados termina sendo colocado em request.FILES
(para mais sobre o objeto request
veja a documentação sobre objetos de requisição e resposta). Este documento explica como arquivos são armazenados no disco ou na memória, e como personalizar o comportamento padrão.
Aviso
Existem riscos de segurança se você estiver aceitando o upload de conteúdo de usuários não confiáveis! Veja o tópico do guia de segurança em Conteúdo carregado por upload de usuários para detalhes.
Consider a simple form containing a FileField
:
from django import forms
class UploadFileForm(forms.Form):
title = forms.CharField(max_length=50)
file = forms.FileField()
Uma “view” que lida com este formulário irá receber o arquivo em request.FILES
, o qual é um dicionário contendo uma chave para cada FileField
(ou ImageField
, ou outra subclasse de FileField
) no formulário. Tal que os dados vindos do formulário acima devem estar acessíveis como request.FILES['file']
.
Note que request.FILES
somente terá dados se o métodos de requisição for POST
e o <form>
que postou a requisição tem o atributo enctype="multipart/form-data"
. Do contrário, o request.FILES
estará vazio.
Na maioria das vezes, você simplesmente passa os dados do arquivo vindos do request
para o formulário como descrito no Binding uploaded files to a form. Isso deve se parecer como:
from django.http import HttpResponseRedirect
from django.shortcuts import render
from .forms import UploadFileForm
# Imaginary function to handle an uploaded file.
from somewhere import handle_uploaded_file
def upload_file(request):
if request.method == 'POST':
form = UploadFileForm(request.POST, request.FILES)
if form.is_valid():
handle_uploaded_file(request.FILES['file'])
return HttpResponseRedirect('/success/url/')
else:
form = UploadFileForm()
return render(request, 'upload.html', {'form': form})
Note que temos que passar request.FILES
para o construtor do “form”; é assim que os dados do arquivo entra no form.
Aqui uma maneira comum que você talvez lide com o “upload” de arquivo:
def handle_uploaded_file(f):
with open('some/file/name.txt', 'wb+') as destination:
for chunk in f.chunks():
destination.write(chunk)
Fazendo “loops” sobre UploadedFile.chunks()
ao invés de usar o read()
garante que grandes arquivos não vão forçar a memória do seu sistema.
Existem alguns outros métodos e atributos disponíveis nos objetos UploadedFile
; veja a UploadedFile
para uma completa referência.
Se você está usando um arquivo em uma Model
com uma FileField
, usando uma ModelForm
faz este processo bem mais fácil. O objeto arquivo será salvo em um local especificado pelo argumento upload_to
do FileField
correspondente quando chamar form.save()
:
from django.http import HttpResponseRedirect
from django.shortcuts import render
from .forms import ModelFormWithFileField
def upload_file(request):
if request.method == 'POST':
form = ModelFormWithFileField(request.POST, request.FILES)
if form.is_valid():
# file is saved
form.save()
return HttpResponseRedirect('/success/url/')
else:
form = ModelFormWithFileField()
return render(request, 'upload.html', {'form': form})
Se você estiver construindo um objeto manualmente, você pode simplesmente assinalar o objeto arquivo vindo do request.FILES
ao campo arquivo do modelo:
from django.http import HttpResponseRedirect
from django.shortcuts import render
from .forms import UploadFileForm
from .models import ModelWithFileField
def upload_file(request):
if request.method == 'POST':
form = UploadFileForm(request.POST, request.FILES)
if form.is_valid():
instance = ModelWithFileField(file_field=request.FILES['file'])
instance.save()
return HttpResponseRedirect('/success/url/')
else:
form = UploadFileForm()
return render(request, 'upload.html', {'form': form})
Se você quer enviar múltiplos arquivos usando um campo de modelo, defina o atributo HTML ``multiple``do “widget” do campo:
from django import forms
class FileFieldForm(forms.Form):
file_field = forms.FileField(widget=forms.ClearableFileInput(attrs={'multiple': True}))
E então sobreescreva o método post
da sua subclasse de FormView
para manipular o envio de múltiplos arquivos:
from django.views.generic.edit import FormView
from .forms import FileFieldForm
class FileFieldView(FormView):
form_class = FileFieldForm
template_name = 'upload.html' # Replace with your template.
success_url = '...' # Replace with your URL or reverse().
def post(self, request, *args, **kwargs):
form_class = self.get_form_class()
form = self.get_form(form_class)
files = request.FILES.getlist('file_field')
if form.is_valid():
for f in files:
... # Do something with each file.
return self.form_valid(form)
else:
return self.form_invalid(form)
Quando um usuário envia um arquivo, o Django passa os dados do arquivo para um * manipular de envio de arquivo* – uma pequena classe que lida com dados de arquivos enquanto este é enviado. Manipuladores de envio de arquivo não inicialmente definidos na definição FILE_UPLOAD_HANDLERS
o qual tem como padrão:
["django.core.files.uploadhandler.MemoryFileUploadHandler",
"django.core.files.uploadhandler.TemporaryFileUploadHandler"]
Juntos as classes class:MemoryFileUploadHandler e TemporaryFileUploadHandler
fornecem ao Django um comportamento padrão para o envio de arquivos que lê arquios pequenos na memória e grandes arquivos no disco.
Você pode escrever manipuladores que personalizem a maneira como Django lida com arquivos. Você pode por exemplo, usar manipuladores personalizados para forçar quotas no nível do usuário, comprimir dados enquanto chegam, renderizar barras de progresso, e mesmo enviar dados diretamente para um outro local de armazenamento sem armazenar localmente. Para maiores detalhes veja o Writing custom upload handlers para saber como você pode personalizar ou mudar completamente o comportamento do envio.
Antes que você salve os arquivos enviados, os dados precisam ser salvos em algum lugar.
Por padrão, se um arquivo enviado é menor que 2.5 megabytes, o Django irá manter todo o conteúdo do arquivo em memória. Isso significa que salvar o arquivo envolve somente uma leitura na memória e uma escrita no disco e portanto muito rápido.
Porém, se o arquivo enviado é muito grande, o Django irá escrever o arquivo enviado em um arquivo temporário no diretório de arquivos temporários do seu sistema. Em uma plataforma Unix isso significa que você pode esperar que o Django crie um arquivo cujo o nome seja algo como /tmp/tmpzfp6I6.upload`. Se o arquivo enviado é grande o bastante, você pode assistir o arquivo crescer em tamanho enquanto o Django transmite o dado para o disco.
Essas especificações – 2.5 megabytes; /tmp
; etc – são simplesmente “padrões razoáveis” que podem ser personalizadas como descrito na próxima seção.
Existem algumas definições que controla como o manipulador de envio de arquivo do Django se comporta. Para detalhes veja o Definições de envio de arquivos.
As vezes uma “view” em particular requer um comportamento diferente para o envio de arquivos. Neste caso, você pode sobrescrever um manipulador baseado no “request” modificando o request.upload_handlers
. Por padrão, esta lista contém os manipuladores informados pelo FILE_UPLOAD_HANDLERS
, mas você pode modificar essa lista como qualquer outra lista.
Por exemplo, suponha que você tenha escrito um ProgressBarUploadHandler
que dá um retorno sobre o progresso do envio do arquivo para algum tipo de “widget” AJAX. Você adicionaria este manipulador a sua lista de manipuladores como aqui:
request.upload_handlers.insert(0, ProgressBarUploadHandler(request))
Você provavelmente poderia querer usar o list.insert()
neste caso (no lugar de append()
) porque o manipulador de barra de progresso precisa rodar antes de qualquer outro. Lembre-se, os manipuladores de envio de arquivo são processados na ordem.
Se você quer trocar completamente os manipuladores de envio de arquivo, você pode apenas assinalar uma nova lista.
request.upload_handlers = [ProgressBarUploadHandler(request)]
Nota
Você só pode modificar manipuladores de envio antes de acessar request.POST
ou request.FILES
– não faz sentido mudar o manipulador de arquivos depois que o tratamento do envio já iniciou. Se você tentar modificar o request.upload_handlers
depois da leitura do ``request.POST``ou ``request.FILES``o Django irá emitir um erro.
Então, você deve sempre modificar os manipuladores de arquivos tão cedo quanto possível nas suas “views”.
Além disso, o request.POST
é acessado pela CsrfViewMiddleware
a qual está habilitada por padrão. Isso significa que você precisa usar o csrf_exempt()
na sua “view”para que seja possível mudar os manipuladores de arquivos. Você precisará então do csrf_protect()
na função que realmente processa a requisição. Note que isso significa que o manipulador de envio do arquivo começa a receber o arquivo antes que as verificações de CSRF sejam feitas. Exemplo de código:
from django.views.decorators.csrf import csrf_exempt, csrf_protect
@csrf_exempt
def upload_file_view(request):
request.upload_handlers.insert(0, ProgressBarUploadHandler(request))
return _upload_file_view(request)
@csrf_protect
def _upload_file_view(request):
... # Process request
mar 30, 2019