L
L
Lopus2016-04-18 20:23:55
Django
Lopus, 2016-04-18 20:23:55

How to upload a file in django with a cyrillic name?

Django refuses to load files in Russian.
Error: 'ascii' codec can't encode characters in position 48-52: ordinal not in range(128)
Any field (FileField or ImageField), django 1.9.4, python 2.7
How can I solve this problem?

Answer the question

In order to leave comments, you need to log in

4 answer(s)
L
Lertmind, 2016-04-18
@Lertmind

The problem is probably not with Django. If you google, then someone has a problem with Nginx stackoverflow.com/a/7602446
Here is another question stackoverflow.com/questions/2457087/unicodedecodee... , in the comment under the answer it is indicated that you need to configure Apache correctly https://code .djangoproject.com/ticket/11030#comment:5
So specify what you are running on: OS and server.
UPD: Here itekblog.com/ascii-codec-cant-encode-characters-in... detailed error options and solutions.

P
Pavel Aksenov, 2016-04-19
@hellmin

this may not be the most reasonable way, but for a quick solution it will do.
The point is to rewrite native-jang upload handlers
in settings.py

FILE_UPLOAD_HANDLERS = (
    'название_аппликейшена.uploadhandler.MemoryFileUploadHandler',
    'название_аппликейшена.uploadhandler.TemporaryFileUploadHandler',
)

And accordingly uploadhandler.py
from io import BytesIO
import os
from django.core.files.uploadedfile import TemporaryUploadedFile, InMemoryUploadedFile
from django.core.files.uploadhandler import FileUploadHandler, StopFutureHandlers
from django.conf import settings
import pytils
import re


def translify(value):
    value = pytils.translit.translify(u"%s" % value)
    value = re.sub("[\W]", "_", value.strip())
    return value


def transliteration_file_name(file_name):
    name, ext = os.path.splitext(file_name)
    return '{0}{1}'.format(translify(name), ext)


class TemporaryFileUploadHandler(FileUploadHandler):
    """
    Upload handler that streams data into a temporary file.
    """

    def __init__(self, *args, **kwargs):
        super(TemporaryFileUploadHandler, self).__init__(*args, **kwargs)

    def new_file(self, field_name, file_name, content_type, content_length, charset=None, content_type_extra=None):
        file_name = transliteration_file_name(file_name)
        super(TemporaryFileUploadHandler, self).new_file(field_name, file_name, content_type,
                                                         content_length, charset, content_type_extra)
        self.file = TemporaryUploadedFile(self.file_name, self.content_type, 0, self.charset, self.content_type_extra)

    def receive_data_chunk(self, raw_data, start):
        self.file.write(raw_data)

    def file_complete(self, file_size):
        self.file.seek(0)
        self.file.size = file_size
        return self.file


class MemoryFileUploadHandler(FileUploadHandler):
    """
    File upload handler to stream uploads into memory (used for small files).
    """

    def handle_raw_input(self, input_data, META, content_length, boundary, encoding=None):
        """
        Use the content_length to signal whether or not this handler should be in use.
        """
        # Check the content-length header to see if we should
        # If the post is too large, we cannot use the Memory handler.
        if content_length > settings.FILE_UPLOAD_MAX_MEMORY_SIZE:
            self.activated = False
        else:
            self.activated = True

    def new_file(self, field_name, file_name, content_type, content_length, charset=None, content_type_extra=None):
        file_name = transliteration_file_name(file_name)
        super(MemoryFileUploadHandler, self).new_file(field_name, file_name, content_type,
                                                      content_length, charset, content_type_extra)
        if self.activated:
            self.file = BytesIO()
            raise StopFutureHandlers()

    def receive_data_chunk(self, raw_data, start):
        """
        Add the data to the BytesIO file.
        """
        if self.activated:
            self.file.write(raw_data)
        else:
            return raw_data

    def file_complete(self, file_size):
        """
        Return a file object if we're activated.
        """
        if not self.activated:
            return

        self.file.seek(0)
        return InMemoryUploadedFile(
            file=self.file,
            field_name=self.field_name,
            name=self.file_name,
            content_type=self.content_type,
            size=file_size,
            charset=self.charset,
            content_type_extra=self.content_type_extra
        )

A
Avrong, 2016-04-18
@Avrong

Translate the file name in transliteration into Latin, or simply assign your own arbitrary name (number).

M
Maxim Barabanov, 2018-04-14
@reb00ter

even though the question is old, I’ll still leave my answer here - you never know who will come in handy
. Such a class is declared as a file storage

import unicodedata2
import pytils
from django.core.files.storage import FileSystemStorage

class ASCIIFileSystemStorage(FileSystemStorage):
    """
    Для автоматической транслитерации всех загружаемых файлов
    """
    def get_valid_name(self, name):
        name_parts = name.split('.')
        name = unicodedata2.normalize('NFKD', pytils.translit.slugify(name_parts[0])).encode('ascii', 'ignore').decode('utf-8')
        name = '{}.{}'.format(name, name_parts[-1])
        return super(ASCIIFileSystemStorage, self).get_valid_name(name)

and is specified in settings as DEFAULT_FILE_STORAGE
regarding the formation of the name, of course, you can still discuss the presence of more than one dot in the file name and make it a little more beautiful, but in general this already solves the main problem

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question