0

I Try to send object and file at the same time to .net api but it dont work.

I am trying to send data with FormData on angular side but FormData does not accept objects. When I don't send it with FormData, I can't receive the Files on the .net side.

How can I send the files and the object to the .net side at the same time?

angular :

const formData = new FormData();
formData.append('type',this.form.get('type').value)
formData.append('productId',this.form.get('product').value);
formData.append('serialNo',this.form.get('serialNo').value);
formData.append('companyId',this.form.get('company').value);
formData.append('unitPrice',this.form.get('unitPrice').value.toString().replace('.',','));
formData.append('description',this.form.get('description').value);
formData.append('properties',this._helperService.mapToObj(this.properties))

const files = this.form.get('files').value as FileList;
const fileArr = Array.from(files);
if(fileArr.length > 0){
  fileArr.forEach(f => formData.append('files',f))
}

this._stockReceiptService.saveStockReceipt(formData).pipe(takeUntil(this.subject)).subscribe(resp => {
  this.success.text = "Stok girişi kaydedildi";
  this.success.fire();
  console.log("Apiden gelen => ", resp);
}, err => {
  this.error.text = "Stok girişi yapılırken hata oluştu";
  this.error.fire();
});

}

SaveStockReceipt Service:

saveStockReceipt(post: any): Observable<any>{
return this._http.post<any>(this.apiURL + '/stock/stockReceipt',post);
}

.net model

public class CreateStockVModel
{
[Required(ErrorMessage = "Type alanı zorunlu alandır.")]
public int Type { get; set; }

[Required(ErrorMessage = "ProductId alanı zorunlu alandır.")]
public int ProductId { get; set; }

[Required(ErrorMessage = "SerialNo alanı zorunlu alandır.")]
public string? SerialNo { get; set; }

[Required(ErrorMessage = "CompanyId alanı zorunlu alandır.")]
public int CompanyId { get; set; }

public decimal? UnitPrice { get; set; }
public string? Description { get; set; }
public List<IFormFile>? Files { get; set; }
public Dictionary<int,int>? Properties { get; set; }
}

controller

[HttpPost]
public async Task<IActionResult> StockReceipt([FromForm] CreateStockVModel vModel)
{
    return Ok(vModel);
}

3 Answers3

0

Maybe you should try to convert C# model to TS interface and put it to saveStockReceipt instead of post: any

yaroslove
  • 1
  • 1
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Apr 15 '22 at 00:22
0

I will write the solution in an abstract way because it will take time. But basically, the workflow will be as follows:

Send the files as a stream with the request to the backend:

<input type="file" name="file" id="file"(change)="onFileChanged($event)" multiple />
....


export class MyComponent {
   public files: any[];
   ...
   onFileChanged(event: any) {
    this.files.push(event.target.files[0]);
  }
  upload() {
    const formData = new FormData();
    this.files.forEach(file => formData.append(this.file.name, this.file, this.file.name));
    ... Apend the rest
    this.httpClient.post(url, formData, { headers: headers }).subscribe(...);
  }
}

You need to read the files in the backend from the request:

   [HttpPost]
   public async Task<IActionResult> StockReceipt([FromForm] CreateStockVModel vModel)
   {
      var files = this.Request.Form.Files[0]; // you can iterate throw other files
      if (files.Length > 0)
      {
          var fileName = ContentDispositionHeaderValue.Parse(files.ContentDisposition).FileName.Trim('"');
          var fullPath = Path.Combine(pathToSave, fileName);
          using (var stream = new FileStream(fullPath, FileMode.Create))
          {
              // here you have the stream
          }
    }
      return Ok();
   }

For more information, see this link: https://code-maze.com/upload-files-dot-net-core-angular/

Samy Sammour
  • 2,298
  • 2
  • 31
  • 66
  • Thank you for your explanation. But my main problem is that when I use [FromForm], I cannot send the Map Object from Angular to the .net side. I want to send files and map object to .net api at the same time. – thecodepanda Apr 14 '22 at 13:04
  • Ahm! You can read the content from the request: Request.Form[key]. as one option – Samy Sammour Apr 14 '22 at 13:13
  • Here you can find a different approach https://dev.to/dotnetsafer/c-11-is-coming-5-features-that-will-blow-your-mind-3o7h – Samy Sammour Apr 14 '22 at 13:14
  • If I were you, I would debug the endpoint and inspect the request to see what it contains and that will lead you to the next step. Because the issue cannot be addressed with one line of code – Samy Sammour Apr 14 '22 at 13:15
  • Here you can find a thread explaining the same issue as yours: https://stackoverflow.com/questions/40629947/receive-file-and-other-form-data-together-in-asp-net-core-web-api-boundary-base – Samy Sammour Apr 14 '22 at 13:16
0

It's because you defined the Files as List but sending files as single file, so to correct the code replace the following:

if(fileArr.length > 0){
  fileArr.forEach(f => formData.append('files',f))
}

with:

let files = [];
if(fileArr.length > 0){
  fileArr.forEach(f => files.push(f));
  formData.append('files', files);
}
Elyas Esna
  • 625
  • 4
  • 19