2011-12-22 10 views
7

Mi chiedo se sia possibile caricare un file zip sul server web di django e inserire i file zip nel database di django senza accedere al file system attuale nel processo (ad esempio estraendo i file nel file zip in un tmp dir e quindi caricarli)File ZipExtFile in Django

Django fornisce una funzione per convertire il file python in file Django, quindi se c'è un modo per convertire ZipExtFile in file python, dovrebbe andare bene.

grazie per l'aiuto! modello

Django:

from django.db import models 

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

Usage:

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 

uscita:

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

[SOLUZIONE] Solution utilizzando un brutto hack:

Come rilevato correttamente dalla Don Quest, file come Clas ses come StringIO o BytesIO dovrebbero rappresentare i dati come un file virtuale. Tuttavia, il costruttore di Django File accetta solo il tipo di file incorporato e nient'altro, sebbene anche le classi simili a file avrebbero svolto il lavoro. L'hack è quello di impostare le variabili in Django :: file 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 

Si prega di tenere commentare se si dispone di una soluzione migliore (tranne che per l'archiviazione personalizzato)

+0

Don ha l'idea giusta, sebbene il file continui a colpire il file system a meno che non si utilizzi l'archiviazione di file personalizzata. –

+0

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

+1

grazie per le risposte. Ma non capisco perché questo non funzionerebbe direttamente, dato che potrei facilmente memorizzare il file zip e un file nel file zip dovrebbe essere lo stesso del file zip originale. – guinny

risposta

6

Senza sapere a molto di Django, posso dire per dare un'occhiata al pacchetto "io". Si potrebbe fare qualcosa di simile:

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

e poi fare tutto ciò che si desidera fare con il vostro ostream Ruscello/File "virtuale".

+0

grazie per la risposta. Ma istream.read() restituisce una matrice di byte invece di una stringa. E non posso decodificare i byte poiché rappresentano un'immagine. – guinny

+0

Sì, era quello che ho provato allora. Anche se il programma non genera un errore, Django ha un problema nel convertire BytesIO in django.file ... – guinny

+0

Nessun problema! Basta usare invece ByteIO: vedi http://docs.python.org/library/io.html?highlight = io # per ulteriori informazioni. –

6

C'è un modo più semplice per fare questo:

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()) 

Con questo, è possibile convertire un file che è stato caricato in memoria direttamente in un file Django. Per un esempio più completo, diciamo che si voleva caricare una serie di file di immagini all'interno di una zip per il file system:

# 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

Ho usato la seguente classe di file Django per evitare la necessità di leggere ZipExtFile in un altro datastructure (StingIO o BytesIO) mentre correttamente impelementando ciò di cui Django ha bisogno per salvare il file direttamente.

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)