一、需求:

Django实现自定义文件名存储文件
	使文件名看起来统一
	避免收到中文文件导致传输、存储等问题
	相同的文件也需要使用不同的文件名

二、实现思路:

思路:
	生成14位随机字母加数字、后10位采用时间戳。从而实现相同文件不同文件名

1.view版:
	在view接收到文件名之后进行重命名,不修改Django默认的文件存储逻辑。从而实现需求
2.upload_to版
	参考链接:https://docs.djangoproject.com/en/3.1/ref/models/fields/#django.db.models.FileField.upload_to
	官方原文:
	upload_to may also be a callable, such as a function. This will be called to obtain the upload path, including the filename. 
	upload_to也可以是可调用的,例如函数。这将被调用以获得上载路径,包括文件名。
3.自定义存储系统:
	暂时没那个必要:只是单纯的修改个文件名而已
4.forms版(20210206补充):
	使用clean方法对文件格式验证并修改文件名

三、具体实现:

  1. views版:

    涉及views.py 、utils.py(自定义)

    • views.py:

      class UploadIdentImageView(APIView):
          def get(self, request, *args, **kwargs):
              return Response({"msg":"ident", "status_code": True})
      
          def post(self, request, *args, **kwargs):
              # from somewhere import handle_uploaded_file
              file_name = request.FILES.get("file", None)
              if not file_name:
                  return Response({"msg": "ident", "status_code": False})
      
              from test_app.utils import custom_file_name
              file_name.name = custom_file_name(file_name)
      
              models_object = models.IdCardImage(image_path=file_name)
              models_object.save()
              print(models_object)
              return Response({"msg":"ident", "status_code": True})
      
    • test_app\utils.py

      def random_str():
          import random
          import time
      
          num_set = [chr(i) for i in range(48, 58)]
          char_set = [chr(i) for i in range(97, 123)]
          total_set = num_set + char_set
          bits = 14
          value_set = "".join(random.sample(total_set, bits))
          return value_set + str(int(time.time()))
      
      def custom_file_name(file_name):
          file_type = str(file_name).split(".")[-1]
          new_file_name = random_str().upper()
      
          return ".".join([new_file_name,file_type])
      
  2. upload_to版:

    涉及models.py。test_app\utils.py沿用上面内容

    • models.py

      def user_directory_path(upload_to):
          def wrapper(instance, filename):
              import datetime
              import os
              from mini_programe.utils import custom_file_name
      
              filename = custom_file_name(filename)
      
              dirname = datetime.datetime.now().strftime(upload_to)
              new_upload_to = os.path.join(dirname,filename)
              return new_upload_to
          return wrapper
      
      
      class IdCardImage(models.Model):
          f_id = models.ForeignKey(Users, on_delete=models.SET_NULL,null=True,verbose_name="用户",related_name="user_id_card_img")
          # image_path = models.FileField(upload_to='media/id_card_img/%Y/%m/')
          image_path = models.FileField(upload_to=user_directory_path('media/id_card_img/%Y/%m/'))
      
      

4.forms版

  • forms.py
class IdCardImageForm(forms.ModelForm):
    class Meta:
        model = models.IdCardImage
        fields = "__all__"

    def clean_image_path(self):   #clean_字段名
        file = self.cleaned_data["image_path"]
        ext = file.name.split(".")[-1].lower()
        
        if ext != "png":
            raise forms.ValidationError("仅允许上传png文件")
        new_file_name = random.randint(111111111,9999999999)
        file.name = "%d.png"%new_file_name
        
        return file
  • models.py
class IdCardImage(models.Model):
    f_id = models.ForeignKey(Users, on_delete=models.SET_NULL,null=True,verbose_name="用户",related_name="user_id_card_img")
    image_path = models.FileField(upload_to='media/id_card_img/%Y/%m/')
  • views.py
class UploadIdentImageView(TemplateView):
    def get(self,....)
        ....
    def post(self,request,*args,**kwargs):
        check_form = forms.IdCardImageForm(request.POST,request.FILES)
        if not check_form.is_valid():
            return JsonResponse({'message': check_form.errors, "code": 400})
        instance = check_form.save()
        return JsonResponse({'message': instance.name + ",保存成功","code":200})
  • upload.html
function getCookie(name) {
    let cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        const cookies = document.cookie.split(';');
        for (let i = 0; i < cookies.length; i++) {
            const cookie = cookies[i].trim();
            // Does this cookie string begin with the name we want?
            if (cookie.substring(0, name.length + 1) === (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}

function upload_pdf() {
    const csrftoken = getCookie('csrftoken');
    
    var form = new FormData();
    form.append("f_id", $("#f_id").val());
    form.append("image_path", $("#image_path")[0].files[0]);
    $.ajax({
        headers: {'X-CSRFToken': csrftoken},
        url: "{% url 'add_data' %}",
        type: "POST",
        data: form,
        
        processData: false,
        contentType: false,
        cache: false,
        
        success: function (result) {
            if (result.code === 200) {
                alert_message(JSON.stringify(result.message));
                    } else {
                        console.log("success", result);
                        alert_message(JSON.stringify(result.message));
                    }
                },
                fail: function (result) {
                    console.log("fail",);
                    alert_message(result.info);
                },
                error: function (result) {
                    alert_message(result.status + ": " + result.statusText);
                }
            });

        }

四、最终效果:

  • 图片:
  • Django 自定义存储上传文件的文件名

五、效果对比:

​ 个人更倾向于方案二或方案四,理由是:views.py文件中的处理函数的处理逻辑应该看起来简单一些。例如采用方案二整理后的view.py看起来像这样:

class UploadIdentImageView(APIView):
    def get(self, request, *args, **kwargs):
        return Response({"msg":"ident", "status_code": True})

    def post(self, request, *args, **kwargs):
        file_name = request.FILES.get("file", None)
        if not file_name:
            return Response({"msg": "ident", "status_code": False})

        models_object = models.IdCardImage(image_path=file_name)
        models_object.save()
        return Response({"msg":"ident", "status_code": True})

生成随机字母加数字的逻辑参考链接:https://blog.csdn.net/u010039418/article/details/86013620