Para un nuevo proyecto en el que me estoy embarcando, he tenido que hacer un script que permitiese subir archivos zip en Django y los descomprimiese. Tras un inicio dubitativo, me he puesto a buscar alguna aplicación o código que ya trajese esta opción hecha y al final he terminado haciendo una mezcla entre código obtenido de django-photo-albums y este trozo de código publicado en DjangoSnippets.
El script escribe el fichero enviado por un formulario POST en disco, comprueba que el fichero es efectivamente un zip y que no está corrupto y lo descomprime, almacenando su contenido en la ruta MEDIA_ROOT configurada en el fichero settings.py del proyecto.
Es bastante simple, pero espero que pueda servir de ayuda a alguien y, sobre todo, que alguien me pueda sugerir mejoras para incluirlas luego en el proyecto.
# -*- coding: utf-8 -*-
import tempfile, os
import settings
from django.http import HttpResponse
from django import forms
from zipfile import ZipFile, BadZipfile
from django.shortcuts import render_to_response
class UploadFileForm(forms.Form):
title = forms.CharField(max_length=50) # No sirve para nada
file = forms.FileField()
# Almacena en disco el fichero
# Comprueba que el zip no está corrupto
# Devuelve el path absoluto a dicho fichero
def clean_file(self):
def ffile_path(uploaded_file):
''' Converts InMemoryUploadedFile to on-disk file so it will have path. '''
try:
return uploaded_file.temporary_file_path()
except AttributeError:
fileno, path = tempfile.mkstemp()
temp_file = os.fdopen(fileno,'w+b')
for chunk in uploaded_file.chunks():
temp_file.write(chunk)
temp_file.close()
return path
path = ffile_path(self.cleaned_data['file'])
try: # Comprobación de que el fichero no está corrupto
zf = ZipFile(path)
bad_file = zf.testzip()
if bad_file:
raise forms.ValidationError(_('El fichero "%s" del ZIP está corrupto.') % bad_file)
zf.close()
except BadZipfile:
raise forms.ValidationError('El fichero subido no es un ZIP.')
return path
def process_file(self):
# Ruta donde se encuentra el fichero
zip_filename = self.cleaned_data['file']
# Lugar donde se alojarán los ficheros descomprimidos
dirname = settings.MEDIA_ROOT
zip = ZipFile(zip_filename)
lista_ficheros = []
# Recorremos todos los ficheros que contiene el zip
for filename in zip.namelist():
ruta_total = os.path.join(dirname, filename)
# Si es un directorio, lo creamos
if filename.endswith('/'):
try: # Don't try to create a directory if exists
os.mkdir(ruta_total)
except:
pass
# Si es un fichero, lo escribimos
else:
outfile = open(ruta_total, 'wb')
outfile.write(zip.read(filename))
outfile.close()
lista_ficheros.append(ruta_total)
zip.close()
os.unlink(zip_filename)
return lista_ficheros
# Vista que muestra el formulario o gestiona la petición POST de éste
def upload_file(request):
if request.method == 'POST':
form = UploadFileForm(request.POST, request.FILES)
# Si el formulario el válido, proceso el fichero
if form.is_valid():
zipdata = request.FILES['file']
title = request.FILES['file'].name
listado = form.process_file()
# Aquí podríamos trabajar con los ficheros subidos
# cuyas rutas totales se encuentran en listado
# Esta es la respuesta que enviamos al usuario una vez
# finalizado el proceso
return HttpResponse("Subido!")
else:
form = UploadFileForm()
return render_to_response('upload.html', {'form': form})