【问题标题】:Angular 5 & Django REST - Issue uploading filesAngular 5 和 Django REST - 上传文件问题
【发布时间】:2018-01-06 19:10:49
【问题描述】:

我开发了一个用户可以处理品牌的 Angular 应用程序。

在创建/更新品牌时,用户还可以上传徽标。所有数据都通过使用 Django REST 框架构建的 REST API 发送到数据库。

使用 Django REST Framework API 网站我可以上传文件,但是当我通过 API 发送数据时使用 Angular 时出现错误。 我还尝试使用 FileReader 将 File 对象编码为 base64,但我从 Django 中得到了同样的错误。

你能帮我理解这个问题吗?

模型

export class Brand {
    id: number;
    name: string;
    description: string;
    is_active: boolean = true;
    is_customer_brand: boolean = false;
    logo_img: Image;
}

export class Image {
    id: number;
    img: string; // URL path to the image (full size)
    img_md: string; // medium size
    img_sm: string; // small
    img_xs: string; // extra-small/thumbnail
}

服务

import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import { Headers, RequestOptions } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';

import { Brand } from './brand';

const endpoint = 'http://127.0.0.1:8000/api/brands/'

@Injectable()
export class BrandService {

  private brands: Array<Brand>;

  constructor(private http: Http) { }

  list(): Observable<Array<Brand>> {
    return this.http.get(endpoint)
        .map(response => {
        this.brands = response.json() as Brand[];
        return response.json();
      })
        .catch(this.handleError);
  }

  create(brand: Brand): Observable<Brand> {
    console.log(brand);
    return this.http.post(endpoint+'create/', brand)
      .map(response => response.json())
      .catch(this.handleError);
  }

  get(id): Observable<Brand> {
    return this.http.get(endpoint+id)
        .map(response => response.json())
        .catch(this.handleError);
  }

  private handleError(error:any, caught:any): any {
    console.log(error, caught);
  }

}

来自浏览器控制台的错误:

"{"logo_img":{"img":["提交的数据不是文件。检查 表单上的编码类型。"]}}"

Django 序列化器

class BrandSerializer(ModelSerializer):
    is_active = BooleanField(required=False)
    logo_img = ImageSerializer(required=False, allow_null=True)

    class Meta:
        model = Brand
        fields = [
            'id',
            'name',
            'description',
            'is_active',
            'is_customer_brand',
            'logo_img',
        ]

    def update(self, instance, validated_data):
        image = validated_data.get('logo_img',None)
        old_image = None
        if image:
            image = image.get('img',None)
            brand_str = validated_data['name'].lower().replace(' ','-')
            ext = validated_data['logo_img']['img'].name.split('.')[-1].lower()
            filename = '{0}.{1}'.format(brand_str,ext)
            user = None
            request = self.context.get('request')
            if request and hasattr(request, 'user'):
                user = request.user
            image_serializer_class = create_image_serializer(path='logos', filename=filename, created_by=user, img_config = {'max_w':3000.0,'max_h':3000.0,'max_file_size':1.5,'to_jpeg':False})
            image_serializer = image_serializer_class(data=validated_data['logo_img'])
            image_serializer.is_valid()
            validated_data['logo_img'] = image_serializer.save()
            old_image = instance.logo_img
        super(BrandSerializer, self).update(instance,validated_data)
        if old_image: # Removing old logo
            old_image.img.delete()
            old_image.img_md.delete()
            old_image.img_sm.delete()
            old_image.img_xs.delete()
            old_image.delete()
        return instance

    def create(self, validated_data):
        image = validated_data.get('logo_img',None)
        print(image)
        if image:
            print(image)
            image = image.get('img',None)
            print(image)
            brand_str = validated_data['name'].lower().replace(' ','-')
            ext = validated_data['logo_img']['img'].name.split('.')[-1].lower()
            filename = '{0}.{1}'.format(brand_str,ext)
            user = None
            request = self.context.get('request')
            if request and hasattr(request, 'user'):
                user = request.user
            image_serializer_class = create_image_serializer(path='logos', filename=filename, created_by=user, img_config = {'max_w':3000.0,'max_h':3000.0,'max_file_size':1.5,'to_jpeg':False})
            image_serializer = image_serializer_class(data=validated_data['logo_img'])
            image_serializer.is_valid()
            validated_data['logo_img'] = image_serializer.save()
        return super(BrandSerializer, self).create(validated_data)

【问题讨论】:

    标签: django angular file-upload django-rest-framework


    【解决方案1】:

    在将新品牌与文件一起发布到服务器时,我有三个主要选择:

    • Base64 对文件进行编码,代价是数据大小增加了大约 33%。
    • 首先在 multipart/form-data POST 中发送文件,然后将 ID 返回给客户端。然后客户端发送带有 ID 的元数据,服务器重新关联文件和元数据。
    • 先发送元数据,返回一个ID给客户端。然后客户端发送带有 ID 的文件,服务器重新关联文件和元数据。

    Base64 编码将涉及不可接受的负载。 所以我选择使用multipart/form-data。

    这是我在 Angular 的服务中实现它的方式:

    create(brand: Brand): Observable<Brand> {
        let headers = new Headers();
        let formData = new FormData(); // Note: FormData values can only be string or File/Blob objects
        Object.entries(brand).forEach(([key, value]) => {
          if (key === 'logo_img') { 
            formData.append('logo_img_file', value.img);
          } else {
            formData.append(key, value);
        });
        return this.http.post(endpoint+'create/', formData)
          .map(response => response.json())
          .catch(this.handleError);
      }
    

    重要提示:由于无法使用FormData 嵌套字段,因此我无法附加formData.append('logo_img', {'img' : FILE_OBJ })。我更改了 API,以便在一个名为 logo_img_file 的字段中接收文件。

    希望我的问题对某人有所帮助。

    【讨论】:

    • 我也面临同样的问题。在我的场景中,我只需要上传一张图片。我不太清楚这种方法,你的意思是提交两个单独的表格?有工作示例吗?
    • 我尝试添加此行以查看是否可以解决问题。它使情况更糟.------------ const headers = new HttpHeaders().set('Content-Type', 'multipart/form-data;');------ --------标头作为参数传递给发布请求调用。
    猜你喜欢
    • 2018-10-06
    • 2017-07-17
    • 1970-01-01
    • 1970-01-01
    • 2018-02-21
    • 1970-01-01
    • 1970-01-01
    • 2016-02-16
    • 2021-05-13
    相关资源
    最近更新 更多