3

I am attempting to create an Angular 5 application that contacts a Java based API to upload a file.

  addImage(file: File): Observable<ImageModel> {

    if (this.authService.checkTokenExpired()) {
      this.authService.refresh().subscribe();
        }

    const httpOptions = {
      headers: new HttpHeaders({
        "Content-Type": undefined
      })
    };
    let form: FormData = new FormData();
    form.append("file", file);
    return this.http.post<ImageModel>(this.imageEndpoint, file, httpOptions);
  }

The HTML portion of the component:

 <h2>Open Ticket</h2>
{{file}}
<form (ngSubmit)="onSubmit()" #addForm="ngForm">
  <div class="form-row">
    <div class="form-group col">
      <select (change)="onPropertyChange()" required name="propertyID" [(ngModel)]="model.propertyID">
        <option *ngFor="let prop of properties" [value]="prop.ID">{{prop.shortName}}</option>
      </select>
    </div>
    <div class="form-group col">
      <select required name="itemID" [(ngModel)]="model.itemID">
        <option  *ngFor="let item of items" [value]="item.ID">{{item.description}}</option>
      </select>
    </div>
  </div>
  <div class="form-group">
    <label for="notes">Notes</label>
    <textarea required class="form-control" name="notes" id="notes" [(ngModel)]="model.notes"></textarea>
  </div>
  <input type="file" [(ngModel)]="file" name="file" />
  <input type="submit" class="btn btn-primary float-right" value="Open">

</form>

The TypeScript portion (which calls "addImage"):

  import { Component, OnInit } from '@angular/core';
import { TicketService } from '../../shared/ticket.service';
import { PropertyModel } from '../../shared/models/property-model';
import { TicketItemModel } from '../../shared/models/ticket-item-model';
import { AddTicketModel } from '../../shared/models/add-ticket-model';

@Component({
  selector: 'app-ticket-add',
  templateUrl: './ticket-add.component.html',
  styleUrls: ['./ticket-add.component.scss']
})
export class TicketAddComponent implements OnInit {

properties : PropertyModel[];
items : TicketItemModel[];
model : AddTicketModel;
file : File;

  constructor(private ticketService : TicketService) { 
    this.model = new AddTicketModel();
  }
  ngOnInit() {
    this.ticketService.getUserProperties()
    .subscribe(res => this.properties = res);

    this.items = [];
  }

  onPropertyChange() {
    this.ticketService.getItems(this.model.propertyID)
    .subscribe((res) => {
      this.items = res;
    })
  }

  onSubmit() {
    if(this.file != null) {
      
      this.ticketService.addImage(this.file).subscribe(res => {console.log(res)});
    } else {
      console.log("File is null");
    }
  }

}

However, I am having issues with the multipart form boundary:

the request was rejected because no multipart boundary was found

To my understanding, the multipart boundary field is one that should be automatically set by the browser, not something we should calculate. I have referenced some questions here including one which advised setting the Content-Type to undefined. However, when I do so, I receive the following error (and rather lengthy stack)

    TypeError: values is undefined
Stack trace:
HttpHeaders/this.lazyInit/<@webpack-internal:///./node_modules/@angular/common/esm5/http.js:163:1
HttpHeaders/this.lazyInit@webpack-internal:///./node_modules/@angular/common/esm5/http.js:157:17
HttpHeaders.prototype.init@webpack-internal:///./node_modules/@angular/common/esm5/http.js:305:17
HttpHeaders.prototype.copyFrom@webpack-internal:///./node_modules/@angular/common/esm5/http.js:324:9
HttpHeaders.prototype.init@webpack-internal:///./node_modules/@angular/common/esm5/http.js:302:17
HttpHeaders.prototype.forEach@webpack-internal:///./node_modules/@angular/common/esm5/http.js:408:9
HttpXhrBackend.prototype.handle/<@webpack-internal:///./node_modules/@angular/common/esm5/http.js:2212:13
Observable.prototype._trySubscribe@webpack-internal:///./node_modules/rxjs/_esm5/Observable.js:177:20
Observable.prototype.subscribe@webpack-internal:///./node_modules/rxjs/_esm5/Observable.js:165:88
CatchOperator.prototype.call@webpack-internal:///./node_modules/rxjs/_esm5/operators/catchError.js:83:16
Observable.prototype.subscribe@webpack-internal:///./node_modules/rxjs/_esm5/Observable.js:162:13
subscribeToResult@webpack-internal:///./node_modules/rxjs/_esm5/util/subscribeToResult.js:32:20
MergeMapSubscriber.prototype._innerSub@webpack-internal:///./node_modules/rxjs/_esm5/operators/mergeMap.js:143:18
MergeMapSubscriber.prototype._tryNext@webpack-internal:///./node_modules/rxjs/_esm5/operators/mergeMap.js:140:9
MergeMapSubscriber.prototype._next@webpack-internal:///./node_modules/rxjs/_esm5/operators/mergeMap.js:123:13
Subscriber.prototype.next@webpack-internal:///./node_modules/rxjs/_esm5/Subscriber.js:100:13
ScalarObservable.prototype._subscribe@webpack-internal:///./node_modules/rxjs/_esm5/observable/ScalarObservable.js:53:13
Observable.prototype._trySubscribe@webpack-internal:///./node_modules/rxjs/_esm5/Observable.js:177:20
Observable.prototype.subscribe@webpack-internal:///./node_modules/rxjs/_esm5/Observable.js:165:88
MergeMapOperator.prototype.call@webpack-internal:///./node_modules/rxjs/_esm5/operators/mergeMap.js:97:16
Observable.prototype.subscribe@webpack-internal:///./node_modules/rxjs/_esm5/Observable.js:162:13
FilterOperator.prototype.call@webpack-internal:///./node_modules/rxjs/_esm5/operators/filter.js:63:16
Observable.prototype.subscribe@webpack-internal:///./node_modules/rxjs/_esm5/Observable.js:162:13
MapOperator.prototype.call@webpack-internal:///./node_modules/rxjs/_esm5/operators/map.js:60:16
Observable.prototype.subscribe@webpack-internal:///./node_modules/rxjs/_esm5/Observable.js:162:13
TicketAddComponent.prototype.onSubmit@webpack-internal:///./src/app/ticket/ticket-add/ticket-add.component.ts:35:13
View_TicketAddComponent_0/<@ng:///TicketModule/TicketAddComponent.ngfactory.js:56:23
handleEvent@webpack-internal:///./node_modules/@angular/core/esm5/core.js:13763:115
callWithDebugContext@webpack-internal:///./node_modules/@angular/core/esm5/core.js:15272:39
debugHandleEvent@webpack-internal:///./node_modules/@angular/core/esm5/core.js:14859:12
dispatchEvent@webpack-internal:///./node_modules/@angular/core/esm5/core.js:10178:16
eventHandlerClosure/<@webpack-internal:///./node_modules/@angular/core/esm5/core.js:12517:38
EventEmitter.prototype.subscribe/schedulerFn<@webpack-internal:///./node_modules/@angular/core/esm5/core.js:4559:36
SafeSubscriber.prototype.__tryOrUnsub@webpack-internal:///./node_modules/rxjs/_esm5/Subscriber.js:248:13
SafeSubscriber.prototype.next@webpack-internal:///./node_modules/rxjs/_esm5/Subscriber.js:195:17
Subscriber.prototype._next@webpack-internal:///./node_modules/rxjs/_esm5/Subscriber.js:136:9
Subscriber.prototype.next@webpack-internal:///./node_modules/rxjs/_esm5/Subscriber.js:100:13
Subject.prototype.next@webpack-internal:///./node_modules/rxjs/_esm5/Subject.js:65:17
EventEmitter.prototype.emit@webpack-internal:///./node_modules/@angular/core/esm5/core.js:4527:24
NgForm.prototype.onSubmit@webpack-internal:///./node_modules/@angular/forms/esm5/forms.js:5840:9
View_TicketAddComponent_0/<@ng:///TicketModule/TicketAddComponent.ngfactory.js:48:23
handleEvent@webpack-internal:///./node_modules/@angular/core/esm5/core.js:13763:115
callWithDebugContext@webpack-internal:///./node_modules/@angular/core/esm5/core.js:15272:39
debugHandleEvent@webpack-internal:///./node_modules/@angular/core/esm5/core.js:14859:12
dispatchEvent@webpack-internal:///./node_modules/@angular/core/esm5/core.js:10178:16
renderEventHandlerClosure/<@webpack-internal:///./node_modules/@angular/core/esm5/core.js:10803:38
decoratePreventDefault/<@webpack-internal:///./node_modules/@angular/platform-browser/esm5/platform-browser.js:2680:53
ZoneDelegate.prototype.invokeTask@webpack-internal:///./node_modules/zone.js/dist/zone.js:421:17
onInvokeTask@webpack-internal:///./node_modules/@angular/core/esm5/core.js:4956:24
ZoneDelegate.prototype.invokeTask@webpack-internal:///./node_modules/zone.js/dist/zone.js:420:17
Zone.prototype.runTask@webpack-internal:///./node_modules/zone.js/dist/zone.js:188:28
ZoneTask.invokeTask@webpack-internal:///./node_modules/zone.js/dist/zone.js:496:24
invokeTask@webpack-internal:///./node_modules/zone.js/dist/zone.js:1540:9
globalZoneAwareCallback@webpack-internal:///./node_modules/zone.js/dist/zone.js:1566:17

What is the proper approach to this issue? I would rather not have to include an extra library or package.

Thanks.

Community
  • 1
  • 1
KellyM
  • 2,472
  • 6
  • 46
  • 90

1 Answers1

0

try to remove httpOption

addImage(file: File): Observable<ImageModel> {

    if (this.authService.checkTokenExpired()) {
      this.authService.refresh().subscribe();
    }


    let form: FormData = new FormData();
    form.append("file", file);
    return this.http.post<ImageModel>(this.imageEndpoint, file);
}

if you set "Content-type" you have yo define boundaty manually

Ging3r
  • 2,010
  • 2
  • 17
  • 26