Angular 5& Django REST - 发布上传文件

时间:2018-01-04 14:40:55

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

我开发了一个Angular应用程序,用户可以在其中处理品牌。

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

使用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“:[”提交的数据不是文件。检查   表单上的编码类型。“]}}”

Angular Error

Django Serializer

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)

1 个答案:

答案 0 :(得分:0)

使用文件向服务器发布新品牌时,我有三个主要选择:

  • 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的一个字段中接收文件。

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