2011-12-22 14 views
7

Me pregunto si hay una manera de cargar un archivo zip al servidor web django y poner los archivos zip en la base de datos django SIN acceder al sistema de archivos real en el proceso (por ejemplo, extraer los archivos en el zip un directorio tmp y luego cárguelos)ZipExtFile to Django File

Django proporciona una función para convertir archivos Python a archivos Django, por lo que si hay una forma de convertir ZipExtFile a archivos Python, debería estar bien.

gracias por ayuda! modelo

Django:

from django.db import models 

class Foo: 
    file = models.FileField(upload_to='somewhere') 

Uso:

from zipfile import ZipFile 
from django.core.exceptions import ValidationError 
from django.core.files import File 
from io import BytesIO 

z = ZipFile('zipFile') 
istream = z.open('subfile') 
ostream = BytesIO(istream.read()) 
tmp = Foo(file=File(ostream)) 
try: 
    tmp.full_clean() 
except Validation, e: 
    print e 

Salida:

{'file': [u'This field cannot be blank.']} 

[Solución] Solución utilizando un feo corte:

Como se ha señalado correctamente Don Quest, clasificado como archivo ses como StringIO o BytesIO deben representar los datos como un archivo virtual. Sin embargo, el constructor de Django File solo acepta el tipo de archivo incorporado y nada más, aunque las clases similares a archivos también habrían hecho el trabajo. El truco es establecer las variables en Django :: Archivo manualmente:

buf = bytesarray(OPENED_ZIP_OBJECT.read(FILE_NAME)) 
tmp_file = BytesIO(buf) 
dummy_file = File(tmp_file) # this line actually fails 
dummy_file.name = SOME_RANDOM_NAME 
dummy_file.size = len(buf) 
dummy_file.file = tmp_file 
# dummy file is now valid 

Se le recuerda al comentar si tiene una mejor solución (excepto para el almacenamiento personalizado)

+0

Don tiene la idea correcta, aunque el archivo aún llegará al sistema de archivos a menos que use almacenamiento de archivos personalizado. –

+0

https://docs.djangoproject.com/en/dev/howto/custom-file-storage/ – tkone

+1

gracias por las respuestas. Pero no entiendo por qué esto no funcionaría directamente, ya que podría almacenar fácilmente el archivo zip y un archivo en el archivo zip debería ser el mismo que el archivo zip original. – guinny

Respuesta

6

Sin conocer mucho acerca de Django, que puedo decir a echar un vistazo al paquete "io". Se podría hacer algo como:

from zipfile import ZipFile 
from io import StringIO 
zname,zipextfile = 'zipcontainer.zip', 'file_in_archive' 
istream = ZipFile(zname).open(zipextfile) 
ostream = StringIO(istream.read()) 

y luego hacer lo que le gustaría hacer con su "virtual" Corriente ostream/Archivo.

+0

gracias por la respuesta. Pero istream.read() le devuelve una matriz de bytes en lugar de una cadena. Y no puedo decodificar los bytes ya que representan una imagen. – guinny

+0

Sí, eso fue lo que probé entonces. Aunque el programa no genera un error, Django tiene un problema al convertir BytesIO a django.file ... – guinny

+0

¡No hay problema! Simplemente use ByteIO en su lugar: vea http://docs.python.org/library/io.html?highlight = io # para obtener más información. –

6

Hay una manera más fácil de hacer esto:

from django.core.files.base import ContentFile 

uploaded_zip = zipfile.ZipFile(uploaded_file, 'r') # ZipFile 

for filename in uploaded_zip.namelist(): 
    with uploaded_zip.open(filename) as f: # ZipExtFile 
     my_django_file = ContentFile(f.read()) 

uso de este, se puede convertir un archivo que se ha subido a la memoria directamente a un archivo de Django. Para un ejemplo más completo, digamos que quería subir una serie de archivos de imagen en el interior de una cremallera al sistema de archivos:

# some_app/models.py 
class Photo(models.Model): 
    image = models.ImageField(upload_to='some/upload/path') 

... 

# Upload code  
from some_app.models import Photo 

for filename in uploaded_zip.namelist(): 
    with uploaded_zip.open(filename) as f: # ZipExtFile 
     new_photo = Photo() 
     new_photo.image.save(filename, ContentFile(f.read(), save=True) 
0

He usado la siguiente clase de archivo de Django para evitar la necesidad de leer ZipExtFile en una otra estructura de datos (StingIO o BytesIO) a la vez que impele adecuadamente lo que Django necesita para guardar el archivo directamente.

from django.core.files.base import File 

class DjangoZipExtFile(File): 
    def __init__(self, zipextfile, zipinfo): 
     self.file = zipextfile 
     self.zipinfo = zipinfo 
     self.mode = 'r' 
     self.name = zipinfo.filename 
     self._size = zipinfo.file_size 

    def seek(self, position): 
     if position != 0: 
      #this will raise an unsupported operation 
      return self.file.seek(position) 
     #TODO if we have already done a read, reopen file 

zipextfile = archive.open(path, 'r') 
zipinfo = archive.getinfo(path) 
djangofile = DjangoZipExtFile(zipextfile, zipinfo) 
storage = DefaultStorage() 
result = storage.save(djangofile.name, djangofile)