使用通用换行符将Django UploadedFile作为UTF-8处理

ebl*_*ume 12 python django

在我的django应用程序中,我提供了一个允许用户上传文件的表单.该文件可以采用多种格式(Excel,CSV),来自各种平台(Mac,Linux,Windows),并以各种编码(ASCII,UTF-8)编码.

出于这个问题的目的,让我们假设我有一个正在接收的视图request.FILES['file'],这是一个InMemoryUploadedFile被调用的实例file.我的问题是InMemoryUploadedFile对象(如file):

  1. 不支持UTF-8编码(我\xef\xbb\xbf在文件的开头看到一个,据我所知是一个标志,意思是'这个文件是UTF-8').
  2. 不支持通用换行符(可能是上传到此系统的大多数文件都需要).

使问题复杂化的是我希望将文件传递给python csv模块,该模块本身不支持Unicode.我很乐意接受避免这个问题的答案 - 一旦我让django玩UTF-8,我相信我可以大胆csv地做同样的事情.(同样,请忽略支持Excel的要求 - 在解决Excel文件之前,我要等到CSV工作.)

我已经尝试使用StringIO,mmap,codec,和任何的各种各样的在访问数据的方法InMemoryUploadedFile的对象.每种方法都产生了不同的错误,到目前为止还没有一种是完美的.这显示了一些我认为最接近的代码:

import csv
import codecs

class CSVParser:
    def __init__(self,file):
        # 'file' is assumed to be an InMemoryUploadedFile object.
        dialect = csv.Sniffer().sniff(codecs.EncodedFile(file,"utf-8").read(1024))
        file.open() # seek to 0
        self.reader = csv.reader(codecs.EncodedFile(file,"utf-8"),
                                 dialect=dialect)
        try:
            self.field_names = self.reader.next()
        except StopIteration:
            # The file was empty - this is not allowed.
            raise ValueError('Unrecognized format (empty file)')

        if len(self.field_names) <= 1:
            # This probably isn't a CSV file at all.
            # Note that the csv module will (incorrectly) parse ALL files, even
            # binary data. This will catch most such files.
            raise ValueError('Unrecognized format (too few columns)')

        # Additional methods snipped, unrelated to issue
Run Code Online (Sandbox Code Playgroud)

请注意,我没有花太多时间在实际的解析算法上,所以它可能效率非常低,现在我更关心的是让编码按预期工作.

问题是尽管被包装在Unicode codecs.EncodedFile文件包装器中,结果也没有被编码.

编辑:事实证明,上面的代码实际上工作.codecs.EncodedFile(file,"utf-8")是票.事实证明我认为它不起作用的原因是我使用的终端不支持UTF-8.活到老,学到老!

ebl*_*ume 7

如上所述,我提供的代码片段实际上是按预期工作的 - 问题出在我的终端上,而不是python编码.

如果您的视图需要访问UTF-8 UploadedFile,您只需使用utf8_file = codecs.EncodedFile(request.FILES['file_field'],"utf-8")正确的编码打开文件对象.

我还注意到,至少对于InMemoryUploadedFiles,通过codecs.EncodedFile包装器打开文件不会重置seek()文件描述符的位置.要返回文件的开头(再次,这可能是InMemoryUploadedFile具体的)我只是用来request.FILES['file_field'].open()seek()位置发回0.

  • 我收到错误`不能在类似字节的对象上使用字符串模式`,行`dialect = csv.Sniffer().sniff(codecs.EncodedFile(file,"utf-8").read(1024))` . 我在 `read()` 之后添加了 `decode()`,然后当我输入 `next(reader)` 时出现错误 `iterator should return strings, not bytes`。怎么了? (3认同)