#31833: Processing file from FileField in a valid form leads to "I/O operation 
on
closed file" exception after posting
--------------------------------------+------------------------
               Reporter:  elmajax     |          Owner:  nobody
                   Type:  Bug         |         Status:  new
              Component:  Forms       |        Version:  3.0
               Severity:  Normal      |       Keywords:
           Triage Stage:  Unreviewed  |      Has patch:  0
    Needs documentation:  0           |    Needs tests:  0
Patch needs improvement:  0           |  Easy pickings:  0
                  UI/UX:  0           |
--------------------------------------+------------------------
 After posting a form with a unique FileField, "I/O operation on closed
 file" exception occurs. Code provoking the error is within the view. I
 doubled checked that form is properly validated from view and validator.

 Strangely the validator class (file is processed differently) doesn't
 throw an  "I/O operation on closed file" exception when file is converted
 with io.TextIOWrapper and processed (see validators.py code below) and
 work properly.

 It seems that somehow file (in memory) is flushed right after being posted
 without any mean to process it further with form.is_valid().

 Tested on Django 2.2.25 and 3.0.8.

 **forms.py**

 {{{

 from .validators import CsvFileValidator

 class FileImportForm(forms.Form):
     headers =
 
["lastname","firstname","gender","title","entity","email","company","address","phone"]
     file = forms.FileField(label='CSV
 file',validators=[CsvFileValidator(headers)])

     def clean_file(self):
         file = self.cleaned_data['file']
         return file
 }}}

 **views.py**

 {{{
 @login_required
 def file_import(request):
     if request.method == 'POST':
         form = FileImportForm(request.POST,request.FILES)

         if form.is_valid():
             if request.FILES['file']:
                 file_post = request.FILES['file']
                 # Offending line below (I/O operation on closed file)
                 file_content = file_post.read().decode('UTF-8')

                 return redirect("/foo")
     else:
         form = FileImportForm()

     return render(request,"file_import.html", { 'form': form })
 }}}

 **validators.py**

 {{{
 import csv

 class CsvFileValidator(object):

     def __init__(self, headers):
         self.headers = headers

     def __call__(self,file):

         file_extension = os.path.splitext(file.name)[1]
         valid_extensions = [ ".csv", ".CSV"]

         if not file_extension.lower() in valid_extensions:
             msg = "Invalid file extension"
             logger.error(msg)
             raise ValidationError("{}".format(msg), code='invalid')

         try:
             csv_file = io.TextIOWrapper(file)
             csv_format = csv.Sniffer().sniff(csv_file.read(1024))
             csv_file.seek(0,0)
         except csv.Error:
             msg = "Invalid CSV file"
             logger.error(msg)
             logger.debug("Exception msg : {}".format(msg))

             raise ValidationError("{}".format(msg), code='invalid')

         csv_reader = csv.reader(csv_file.read().splitlines(),
 dialect=csv_format,delimiter=";")

         for r_index, row in enumerate(csv_reader):

             if r_index == 0:
                 if sorted(self.headers) != sorted(row):
                     msg = "Invalid or missing CSV headers"
                     logger.error(msg)

                     raise ValidationError("{}".format(msg),
 code='invalid')

             if not "".join(str(field) for field in row):
                 continue

         msg = "Valid CSV file"
         logger.debug(msg)

         return True

     def __eq__(self,other):
         return (
             isinstance(other, self.__class__)
             and self.headers == other.headers
         )
 }}}

-- 
Ticket URL: <https://code.djangoproject.com/ticket/31833>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/050.1b7a7b358d48734c29e4668465021b72%40djangoproject.com.

Reply via email to