上传包含附加信息的文件(Angular 8 到 C# Core 3)

Upload File(s) with Additional Information (Angular 8 to C# Core 3)

我终于想出了如何将文件从 Angular 8 前端上传到 C# .Net Core 3.0 后端 API 控制器。但是为了得到我完全需要的东西,我还有一个 class 来定义文件内容(例如 header 行的#,列值等等......)由需要的用户设置也被传入。

在客户端,我只是创建一个 FormData object 并将文件推送到其中,然后将其发送到后端。如果我只发送此 class 然后在后端将其作为 "IFormFileCollection" 接收,则效果很好。但是,如果我将 FormData 和附加的 class 放入包装器 class 中,则会出现错误(posted 在底部)。这里我 post 代码不起作用。

这是我发送的客户端代码:

export interface FileProvider 
{
  formData: FormData;
  testString: string;
}

然后我的 TypeScript 代码发送到后端:

 async UploadFiles() {

    try {

      let files: File[] = this.files;
      let myFormData: FormData = new FormData();

      for (let i = 0; i < files.length; i++) {
        let file: File = files[i];
        myFormData.append('file', file, file.name);
      }

      /** Wrap this in a class. */
      let fileProvider: FileProvider = {
        formData: myFormData,
        testString: "This is just a test string... but will be a class"
      }

      let promise = new Promise((resolve, reject) => {
        this.dataService.UploadData(fileProvider).subscribe(data => resolve(data), error => reject(error))
      });

      let result = await promise;
      alert("Successfully loaded Data");
    }
    catch (error) {
      alert(error.message + ", Status: " + error.status + ",  OK: " + error.ok + ",  " + error.error);
    }
  }

我的小数据服务post方法:

  UploadData(fileProvider: FileProvider) {
    let path: string = this.api + 'wells/formdata';
    return this.http.post(path, fileProvider);
  }

还有我的 C# .Net Core 3 后端控制器代码:

        [Serializable]
        public class FileProvider
        {
            public IFormCollection FormData { get; set; }
            public string TestString { get; set; }
        }


     [HttpPost("formdata"), DisableRequestSizeLimit]
        public IActionResult Upload(FileProvider fileProvider)
        {
            try
            {

                IFormFileCollection fileCollection = fileProvider.FormData.Files;
                string testString = fileProvider.TestString;


                foreach (IFormFile file in fileCollection)
                {

                    /// Read at a line a time.
                    StringBuilder lineAtATime = new StringBuilder();
                    using (var reader = new StreamReader(file.OpenReadStream()))
                    {
                        while (reader.Peek() >= 0)
                        {
                            string line = reader.ReadLine();
                            lineAtATime.Append(line);
                        }
                    }
                    string textByLines = lineAtATime.ToString();


                }
                return Ok();
            }
            catch (Exception ex)
            {
                return StatusCode(500, "Internal server error: " + ex.Message);
            }
        }

最后抛出的错误如下:

"Cannot deserialize the current JSON object (e.g. {"name":"value"}) 输入 'Microsoft.AspNetCore.Http.IFormCollection' 因为该类型需要 JSON 数组(例如 [1,2,3])才能正确反序列化。 要修复此错误,请将 JSON 更改为 JSON 数组(例如 [1,2,3])或更改反序列化类型,以便它 是一个普通的 .NET 类型(例如,不是像整数这样的原始类型,不是像数组或列表这样的 collection 类型),它可以是 从 JSON object 反序列化。也可以将 JsonObjectAttribute 添加到类型以强制它从 JSON object 反序列化。 路径 'formData',第 1 行,位置 14。“

如何传入两个参数 classes?

谢谢!!

原因是您发送的是 json 类对象 而不是 multipart/form-data[=46= 中的有效负载] 格式。因为 JSON 不能表示二进制字节(文件),所以你永远不能用那种方式来表示。

如何修复:

无需上传自定义 FileProvider (json),只需 testString 字段附加到 FormData,然后直接上传FormData.

服务器端

FormData 属性 替换为 IList<IFormFile> Files:

[Serializable]
public class FileProvider
{
    public string TestString { get; set; }
    public IFormCollection FormData { get; set; }
    public IList<IFormFile> Files { get; set; }
}

public IActionResult Upload([FromForm]FileProvider fileProvider)
{
    var files = fileProvider.Files;
    var testString = fileProvider.TestString;
    ...
}

客户端

更改您的 Service 以接收 FormData 类型的参数:

  UploadData(fileProvider: FileProvider) {
  UploadData(formdata: FormData) {
    let path: string = this.api+ 'wells/formdata';
    return this.http.post(path, formdata);
  }

最后,直接将额外字段附加到 FormData :

async UploadFiles() {
    try {

      let files: File[] = this.files;
      let myFormData: FormData = new FormData();

      for (let i = 0; i < files.length; i++) {
        let file: File = files[i];
        myFormData.append('file', file, file.name);
        myFormData.append('files', file, file.name);    // the filed name is `files` because the server side declares a `Files` property
      }
      myFormData.append("testString", "This is just a test string... but will be a class");       // add extra fields 
      // ... add more fields as you like

      let fileProvider: FileProvider = {
        formData: myFormData,
        testString: "This is just a test string... but will be a class"
      }

      let promise = new Promise((resolve, reject) => {
        this.dataService.UploadData(fileProvider).subscribe(data => resolve(data), error => reject(error))
      });

      let resp = await this.dataService.UploadData(myFormData).toPromise();
      alert("Successfully loaded Data");
    }
    catch (error) {
      alert(error.message + ", Status: " + error.status + ",  OK: " + error.ok + ",  " + error.error);
    }
}