Django Rest Framework + Angular 2 - 上传多个文件

时间:2017-09-12 14:26:11

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

我正在使用Django Rest Framework作为我的后端,而Angular 2作为我的前端。我在Angular中有这个页面,我创建了一个表单:

createResourcesForm() {
  this.resourcesForm = this.formBuilder.group({
    resources: this.formBuilder.array([
      this.formBuilder.group({
        title: ['', Validators.compose([Validators.required])],
        file: ['', Validators.compose([])],
      })
    ])
  })
}

如您所见,表单由FormArray组成,其中每个元素都有两个输入:标题和文件 - 分别是文本输入和文件输入。在提交表单时,我正在尝试将数据发送到Django但我收到错误Missing filename. Request should include a Content-Disposition header with a filename parameter.。我可以轻松设置它,但我希望收到{title,file}列表,那么如何设置多个文件名?还有其他想法我怎么能这样做?

Django Rest Framework中的错误来自parse中的FileUploadParser方法。

我没有在这里粘贴任何Python代码,因为它是标准的ListCreateAPIView,没什么特别的。这是我的序列化器:

class ResourceCreateSerializer2(serializers.Serializer):
    author_pk = serializers.IntegerField(required=False)
    author_first_name = serializers.CharField(max_length=50, required=False)
    author_last_name = serializers.CharField(max_length=50, required=False)
    resources = ResourceWithoutAuthorSerializer(many=True)

class ResourceWithoutAuthorSerializer(serializers.ModelSerializer):
    instruments = InstrumentSerializer(many=True)

    class Meta:
        model = MusicResource
        fields = ['title', 'file', 'instruments']

不介意其他字段,它们被发送得很好(就像文件一样)。还有一件事要说 - 我在发送数据之前在Angular中添加了content-type标题。

更新1 这是我上传文件的方法(Angular 2):

get value() {
  let formData = new FormData();

  for (let i = 0; i < this.resources.length; i++) {
    let resource = this.resources.value[i];
    let fileName = resource.file;
    let fileInputs = $('input[type="file"]').filter(function () {
      return this.value == fileName;
    });

    if (fileInputs.length == 0) {
      return null;
    }

    let fileInput = <HTMLInputElement>fileInputs[0];

    formData.append('resources-' + i + '-title', resource.title);
    formData.append('resources-' + i + '-file', fileInput.files[0], fileInput.files[0].name);

    for (let j = 0; j < this.instrumentsSelect.value.length; j++) {
      formData.append('resources-' + i + '-instruments', this.instrumentsSelect.value[j]);
    }
  }

  return formData;
}

然后

this.musicResourcesService.addMusicResource(toSend).subscribe(
  data => console.log('successfuly added resources'),
  err => console.log('MusicResourcesAddComponent', 'onMusicResourceFormSubmit', err)
);

addMusicResource(data) {
  let headers = new Headers({});
  headers.append('Content-Type', 'multipart/form-data');
  headers.append('Accept', 'application/json');
  let options = new RequestOptions({headers});

  return this.api.post('resources/resources/list_create/', data, true, options);
}

public post(url: any, payload: any, noToken?, options?: any): Observable<any> {
  const provider = noToken ? this.http : this.authHttp;
  const fulLUrl = this.conf.getAPIUrl() + url;
  return provider.post(fulLUrl, payload, options)
    .delay(100)
    .map(this.extractData)
    .catch(this.handleError).share();
}

1 个答案:

答案 0 :(得分:1)

我不喜欢@Robert的回答,也没有接受任何其他想法所以经过几个小时的研究后发现我错过了两件事:

  1. 解析器应该已设置为MultiPartParser,而不是FileUploadParser
  2. 无需手动设置Content-Type标头,它将自动填充我缺少的边界
  3. 另外,为了确保Django收到所有数据并理解它,我不得不改变

    formData.append('resources[' + i + ']title', resource.title);
    

    类似的行
    document.getElementById