Quando o Django manipula um upload de arquivo, os dados do arquivo são acessíveis pelo request.FILES (para saber mais sobre o objeto request, veja a documentação dos objetos request e response). Este documento explica como os arquivos são armazenados no disco e na memória, e como personalizar o comportamento padrão.
Considere um simples formulário contendo um FileField:
from django import forms
class UploadFileForm(forms.Form):
title = forms.CharField(max_length=50)
file = forms.FileField()
Um view que manipula este form receberá os dados do arquivo no request.FILES, é um dicionário contendo uma chave para cada FileField (ou ImageField, outra subclasse de FileField) no formulário. De modo que os dados do formulário acima seriam acessíveis como request.FILES['file'].
Note que request.FILES somente conterá dados se o método request for POST e o <form> postado tiver um atributo enctype="multipart/form-data". Caso contrário, request.FILES será vazio.
Na maior parte do tempo, você simplesmente passará os dados do arquivo do request como descrito em Vinculando arquivos enviados a um formulário. Isso deveria parecer com algo tipo:
from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response
# Função imaginária para manipular um upload de arquivo.
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_to_response('upload.html', {'form': form})
Observe que nós temos de passar request.FILES para o construtor do formulário, é assim que o dos dados do arquivo são incorporados pelo form.
A peça final do quebra-cabeça é manipular os dados do arquivo do request.FILES. Cada entrada neste dicionário é um objeto UploadedFile -- uma simples classe que envolve o arquivo enviado. Você pode habitualmente usar um destes métodos para acessar o conteúdo submetido:
Um gerador retornando pedaços do arquivo. Se multiple_chunks() for True, você deve usar este método num loop ao invés de read().
Na prática, é frequentemente muito mais fácil usar chunks() o tempo todo; veja o exemplo abaixo.
Existem uns outros métodos e atributos disponíveis nos objetos UploadedFile; veja Objeto UploadedFile para uma referência completa.
Colando tudo isso junto, temos uma forma comum de manipular o upload de um arquivo:
def handle_uploaded_file(f):
destination = open('some/file/name.txt', 'wb+')
for chunk in f.chunks():
destination.write(chunk)
destination.close()
Iterando sobre UploadedFile.chunks() no lugar de usar read() assegura que arquivos grandes não sobrecarreguem a memória do seu sistema.
Antes de salvar os arquivos enviados, os dados precisam ser armazenados em algum lugar.
Por padrão, se um upload é menor que 2.5 megabytes, o Django irá tratar o conteúdo inteiramente na memória. Isto significa que salvar o arquivo envolve somente ler da memória e escrever no disco e isto é muito rápido.
No entando, se um upload é muito grande, o Django armazenará o arquivo enviado num arquivo temporário, dentro do diretório temporário do seu sistema. Numa plataforma baseada no Unix, isto significa que o Django gerará um arquivo com um nome tipo /tmp/tmpzfp6I6.upload. Se um arquivo é garnde o suficiente, você pode assistir o crescimento deste arquivo este arquivo a medida que o Django realiza a transferência para o disco.
Estas especificidades -- 2.5 megabytes; /tmp; etc. -- são simplesmente "padrões razoáveis". Leia sobre, para saber detalhes de como você pode personalizar ou substituir completamente o comportamento do upload.
Três configurações controlam o comportamento do upload de arquivo do Django:
- FILE_UPLOAD_MAX_MEMORY_SIZE
O tamanho máximo, em bytes, para arquivos que serão armazenados em memória. Arquivos maiores que FILE_UPLOAD_MAX_MEMORY_SIZE serão manipulados por disco.
Padrão é 2.5 megabytes.
- FILE_UPLOAD_TEMP_DIR
O diretório onde os arquivos enviado maiores que o FILE_UPLOAD_MAX_MEMORY_SIZE serão armazenados.
Ele segue o padrão do diretório temporário do seu sistema (i.e. /tmp nos sistemas baseados no Unix).
- FILE_UPLOAD_PERMISSIONS
O modo número (i.e. 0644) para setar o novo arquivo enviado. Para mais informações sobre o que estes modos significam, veja a documentação do os.chmod
Se este não for dado ou for None, você terá o comportamento inerente do sistema operacional. Na maioria das plataformas, os arquivos temporários são salvos usando o padrão do sistema umask.
Warning
Se você não está familiarizado com os modos de arquivos, por favor note que o 0 que fica na frente é muito importante: ele indica um número octal, que é a forma como os modos são especificados. Se você tentar usar 644, você receberá um comportamento totalmente incorreto.
Sempre prefixe o modo com um 0.
O manipulador atual para os arquivos enviados. Mudando esta configuração permite uma completa personalização -- ou mesmo substituição -- do processo de upload do Django. Veja manipuladores de upload abaixo, para detalhes.
O padrão é:
("django.core.files.uploadhandler.MemoryFileUploadHandler", "django.core.files.uploadhandler.TemporaryFileUploadHandler",)O que significa "tente fazer o upload em memória primeiro, e então faça com arquivo temporário."
Além de extender a classe File, todo objeto UploadedFile define os seguintes métodos/atributos:
Note
Como arquivos regulares do Python, você pode ler o arquivos linha-por-linha simplesmente iterando sobre o arquivos enviado:
for line in uploadedfile:
do_something_with(line)
Entretanto, ao contrário dos arquivos padrões do Python, o UploadedFile somente entende \n (também conhecino como "Unix-style") como final de linha. Se você sabe que precisa manipular arquivos diferentes tipos de finalização de linha, então você precisará fazê-lo no seu view.
When a user uploads a file, Django passes off the file data to an upload handler -- a small class that handles file data as it gets uploaded. Upload handlers are initially defined in the FILE_UPLOAD_HANDLERS setting, which defaults to:
("django.core.files.uploadhandler.MemoryFileUploadHandler",
"django.core.files.uploadhandler.TemporaryFileUploadHandler",)
Together the MemoryFileUploadHandler and TemporaryFileUploadHandler provide Django's default file upload behavior of reading small files into memory and large ones onto disk.
You can write custom handlers that customize how Django handles files. You could, for example, use custom handlers to enforce user-level quotas, compress data on the fly, render progress bars, and even send data to another storage location directly without storing it locally.
Algumas vezse views particulares requerem um comportamento de upload diferente. Nestes casos, você pode sobrescrever os manipuladores de upload, basicamente modificando o request.upload_handlers. Por padrão, esta lista conterá os mainpuladores de upload dados pelo FILE_UPLOAD_HANDLERS, mas você pode modificar a lista como se você qualquer outra lista.
Para instâncias, suponhamos que você tenha estrico um ProgressBarUploadHandler que provê uma resposta sobre o progresso do upload para algum tipo de widget AJAX. Você poderia adicionar este handler ao seu manipulador de upload desta forma:
request.upload_handlers.insert(0, ProgressBarUploadHandler())
Você poderia provavelmente querer usar o list.insert() neste caso (ao invés de append()) porquê um manipulador de barra de progresso precisaria rodar antes de qualquer outro manipulador. Lembre-se, os manipuladores de upload são processados em ordem.
Se você deseja substituir o manipulador de upload completamente, você pode somente atribuir uma nova lista:
request.upload_handlers = [ProgressBarUploadHandler()]
Note
Você pode somente modificar mainpuladores de upload antes deles acessarem o request.POST ou request.FILES -- pois não faz sentido mudar o manipulador depois que ele já tiver iniciado. Se você tentar modificar request.upload_handlers depois que estiver lendo o request.POST ou request.FILES o Django gerará um erro.
Sendo assim, você deve sempre modificar os manipuladores de upload o mais cedo possível dentro do seu view.
Todo manipulador de upload de arquivo deve ser uma subclasse de django.core.files.uploadhandler.FileUploadHandler. Você pode definir o manipulador de upload aonde você desejar.
Manipuladores de arquivos personalizados devem definir os seguintes métodos:
``FileUploadHandler.receive_data_chunk(self, raw_data, start)``
Recebe um "pedaço" de dado do arquivos enviado.
``raw_data`` é uma byte string contendo o dado enviado.
``start`` é a posição no arquivo onde este pedaço ``raw_data`` começa.
Os dados que você retornar serão alimentados num método subsequente do
manipulador de upload ``receive_data_chunck``. Desta forma, um
manipulador pode ser um "filter" para outros manipuladores.
Retorna ``None`` do ``receive_data_chunk`` lembrando o manipulador de
upload para começar este pedaço. Isso é usual se você estiver
armazenando o arquivo enviado você mesmo, e não quer que os próximos
manipuladores façam uma cópia dos dados.
Se você lançar uma exceção ``StopUpload`` ou ``SkipFile``, o upload será
abortado ou o arquivo pulado.
``FileUploadHandler.file_complete(self, file_size)``
Chamado quando o arquivos finalizou o upload.
O manipulador deve retornar um objeto ``UploadedFile`` que será
armazenado em ``request.FILES``. Manipuladores podem também retornar
``None`` para indicar que o objeto ``UploadedFile`` deveria vir de uma
manipulador subsequente.
Um manipulador de upload personalizado também definie qualquer um dos sequintes métodos ou atributos opcionais:
Tamanho, em bytes, dos "pedaços" que o Django deve armazenar em memória dentro do manipulador. Isto é, este atributo controla o tamanho dos pedaços dentro do FileUploadHandler.receive_data_chunk.
Para uma performance máxima o tamanho dos arquivos devem ser divisíveis por 4 e não devem exceder 2GB (231 bytes). Quando existem vários tamanhos de pedaços fornecididos por vários manipuladores, o Django usará o menor tamanho definido para qualquer manipulador.
O tamanho padrão é 64*210 bytes, ou 64KB.
Um callback sinalizando que um novo upload de arquivo começou. Este é chamado antes de qualquer dado ser alimentado qualquer manipulador de upload.
field_name é uma string com o nome do campo de arquivo <input>.
file_name é o nome do arquivo unicode que foi fornecido pelo navegador.
content_type é o tipo MIME fornecido pelo navegador -- E.g 'image/jpeg'.
content_length é o comprimento da imagem dado pelo navegador. As vezes ele não será fornecido e será None., None caso contrário.
charset é o conjunto de caracteres (i.e. utf8) dado pelo navegador. Como o content_length, algumas vezes ele não será fornecido.
Este método pode lançar uma exceção StopFutureHandlers para previnir que os próximos handlers de manipular este arquivo.
Permite o manipulador sobrescrever completamente o parseamento da entrada pura do HTTP.
input_data é um objeto, tipo arquivo, que suporta leitura pelo read().
META é o mesmo objeto como request.META.
content_length é o comprimento de dados no input_data. Não leia mais bytes do content_length do que tem no input_data.
boundary é o limite do MIME para esta requisição.
encoding é a codificação da requisição.
Retorna None se você quer que o manipulador de upload continue, ou uma tupla com (POST, FILES) se você quiser retornar a nova estrutura de dados adequada para a requisição diretamente.
Dec 26, 2011