O upload de arquivos em aplicações web geralmente segue dois padrões principais: submissão via formulário HTML tradicional e envio assíncrono via AJAX.
Upload via Formulário HTML
O envio de arquivos via formulário HTML requer configurações específicas para que o servidor possa interpretar corretamente os dados binários. A especificação RFC 1867 introduziu essa funcionalidade, definindo que o método HTTP deve ser POST e o atributo enctype do formulário deve ser configurado para multipart/form-data. Além disso, um campo de entrada do tipo file é necessário.
Ao usar enctype="multipart/form-data", a requisição HTTP utiliza um delimitador (boundary) para separar os diferentes campos e o próprio arquivo dentro do corpo da requisição. Este delimitador é uma string aleatória que garante a integridade dos dados, prevenindo conflitos com caracteres que possam existir dentro do conteúdo do arquivo. O servidor utiliza esse delimitador para segmentar e processar os dados corretamente.
No lado do servidor Django, o arquivo enviado pode ser acessado através de request.FILES. Os dados de texto e outros campos do formulário estão disponíveis em request.POST.
Exemplo de Formulário (Frontend)
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
<input type="file" name="arquivo_carregado">
<input type="text" name="nome_usuario">
<input type="submit" value="Enviar">
</form>
Exemplo de View (Backend - Django)
from django.shortcuts import render, redirect
from django.db import transaction
import xlrd # Supondo que xlrd seja usado para processar arquivos Excel
def importar_dados(request, projeto_pk):
""" Importa dados de um arquivo Excel para um projeto específico. """
if request.method == 'POST':
try:
with transaction.atomic():
arquivo_excel = request.FILES.get('arquivo_carregado')
if not arquivo_excel:
return render(request, 'template_importacao.html', {"projeto_pk": projeto_pk, "erro": "Nenhum arquivo foi selecionado."})
# Lê o conteúdo do arquivo sem salvá-lo em disco temporariamente
workbook = xlrd.open_workbook(filename=None, file_contents=arquivo_excel.read())
sheet = workbook.sheet_by_index(0) # Pega a primeira planilha
# Processa os dados da planilha (exemplo: iterando sobre as linhas)
for i in range(1, sheet.nrows):
dados_linha = sheet.row_values(i)
print(f"Processando linha {i}: {dados_linha}")
# Aqui você adicionaria a lógica para salvar os dados no banco de dados
return render(request, 'template_importacao.html', {"projeto_pk": projeto_pk, "mensagem_sucesso": "Upload e processamento bem-sucedidos!"})
except xlrd.XLRDError:
return render(request, 'template_importacao.html', {"projeto_pk": projeto_pk, "erro": "Formato de arquivo inválido. Suportamos apenas arquivos Excel (.xls, .xlsx)."})
except Exception as e:
print(f"Erro durante o processamento: {e}")
return render(request, 'template_importacao.html', {"projeto_pk": projeto_pk, "erro": f"Ocorreu um erro inesperado: {e}"})
return render(request, 'template_importacao.html', {"projeto_pk": projeto_pk, "erro": ""})
Upload via AJAX
O envio de aruqivos via AJAX oferece uma experiência de usuário mais fluida, sem a necessidade de recarregar a página inteira. Para isso, utiliza-se o objeto FormData no JavaScript, que permite construir um conjnuto de pares chave/valor a serem enviados, semelhante a como um formulário HTML faz. É crucial incluir o token CSRF para a segurança da aplicação.
Ao configurar a requisição AJAX, é importante definir processData: false e contentType: false. O primeiro impede que o jQuery serialize os dados automaticamente, e o segundo instrui o navegador a não definir o cabeçalho Content-Type, permitindo que o servidor defina o delimitador correto para multipart/form-data.
Exemplo de Código (Frontend - JavaScript com jQuery)
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<title>Upload AJAX</title>
</head>
<body>
<div>
{% csrf_token %}
<input type="file" id="arquivoAjax">
<button id="btnUploadAjax">Enviar via AJAX</button>
</div>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
$("#btnUploadAjax").click(function () {
var formData = new FormData();
var arquivoSelecionado = document.getElementById('arquivoAjax').files[0];
if (!arquivoSelecionado) {
alert("Por favor, selecione um arquivo para enviar.");
return;
}
formData.append('arquivo_upload', arquivoSelecionado);
formData.append("csrfmiddlewaretoken", $("[name='csrfmiddlewaretoken']").val());
formData.append('usuario_remetente', 'Alice'); // Exemplo de dado adicional
$.ajax({
url: "/api/upload/", // URL da sua view de upload
type: "POST",
data: formData,
processData: false,
contentType: false,
success: function (resposta) {
console.log("Resposta do servidor:", resposta);
alert("Upload realizado com sucesso!");
},
error: function (jqXHR, textStatus, errorThrown) {
console.error("Erro no upload:", textStatus, errorThrown);
alert("Falha no upload do arquivo.");
}
});
});
</script>
</body>
</html>
Exemplo de View (Backend - Django)
from django.shortcuts import render
from django.http import JsonResponse
import xlrd # Exemplo com xlrd
def handle_ajax_upload(request):
if request.is_ajax() and request.method == 'POST':
# Acessa o arquivo enviado
arquivo = request.FILES.get('arquivo_upload')
# Dados de texto adicionais
usuario = request.POST.get('usuario_remetente')
if not arquivo:
return JsonResponse({"status": "erro", "mensagem": "Nenhum arquivo foi recebido."}, status=400)
print(f"Arquivo recebido: {arquivo.name} ({arquivo.content_type})")
print(f"Usuário remetente: {usuario}")
try:
# Exemplo: processar arquivo Excel
workbook = xlrd.open_workbook(filename=None, file_contents=arquivo.read())
sheet = workbook.sheet_by_index(0)
print(f"Primeira linha da planilha: {sheet.row_values(0)}")
# Lógica de processamento...
return JsonResponse({"status": "sucesso", "mensagem": "Arquivo processado com êxito.", "dados_recebidos": {"usuario": usuario}})
except xlrd.XLRDError:
return JsonResponse({"status": "erro", "mensagem": "Formato de arquivo não suportado."}, status=400)
except Exception as e:
print(f"Erro no processamento do arquivo: {e}")
return JsonResponse({"status": "erro", "mensagem": "Erro interno no servidor."}, status=500)
return render(request, 'template_upload_ajax.html')
Download de Arquivos
O Django oferece classes eficientes para servir arquivos estáticos, como StreamingHttpResponse e FileResponse. Ambas são adequadas para download, com FileResponse sendo geralmente preferível para arquivos locais, pois pode otimizar o envio.
Ao configurar a resposta de download, é importante definir os cabeçalhos Content-Type e Content-Disposition. O Content-Type especifica o tipo MIME do arquivo, e Content-Disposition: attachment;filename="nome_arquivo.ext" instrui o navegador a baixar o arquivo com o nome especificado.
Download com StreamingHttpResponse
from django.http import StreamingHttpResponse
import os
def download_stream(request):
caminho_arquivo = 'caminho/para/seu/arquivo.txt' # Substitua pelo caminho real
if os.path.exists(caminho_arquivo):
arquivo_aberto = open(caminho_arquivo, 'rb')
resposta = StreamingHttpResponse(arquivo_aberto, content_type='application/octet-stream')
resposta['Content-Disposition'] = f'attachment; filename="{os.path.basename(caminho_arquivo)}"'
return resposta
else:
return JsonResponse({"erro": "Arquivo não encontrado"}, status=404)
Download com FileResponse
from django.http import FileResponse
import os
def download_file(request):
caminho_arquivo = 'caminho/para/seu/arquivo.txt' # Substitua pelo caminho real
if os.path.exists(caminho_arquivo):
arquivo_aberto = open(caminho_arquivo, 'rb')
resposta = FileResponse(arquivo_aberto, content_type='application/octet-stream')
resposta['Content-Disposition'] = f'attachment; filename="{os.path.basename(caminho_arquivo)}"'
return resposta
else:
return JsonResponse({"erro": "Arquivo não encontrado"}, status=404)
Tratamento de Nomes de Arquivo com Caracteres Especiais (Ex: Chineses)
Nomes de arquivo contendo caracteres não-ASCII podem causar problemas em alguns navegadores. O Django oferece a função escape_uri_path para codificar corretamente esses nomes, garantindo compatibilidade.
from django.http import FileResponse
from django.utils.encoding import escape_uri_path
import os
def download_arquivo_nome_especial(request):
nome_arquivo_original = "Arquivo com Caracteres Especiais.txt" # Exemplo
caminho_arquivo = 'caminho/para/seu/arquivo.txt' # Caminho real do arquivo
if os.path.exists(caminho_arquivo):
arquivo_aberto = open(caminho_arquivo, 'rb')
resposta = FileResponse(arquivo_aberto, content_type='application/octet-stream')
# Codifica o nome do arquivo para garantir compatibilidade
nome_arquivo_codificado = escape_uri_path(nome_arquivo_original)
resposta['Content-Disposition'] = f'attachment; filename="{nome_arquivo_codificado}"'
return resposta
else:
return JsonResponse({"erro": "Arquivo não encontrado"}, status=404)