Load a file via JHipster REST service using HTTPie or Curl

679 Views Asked by At

I have made a JHipster (v5.3.1) app that has an entity Attachment that contains a Blob field called fileAttachment. Using the generated Angular 6 web front end I am able to load files to the app. I would like to do the same using command line (either HTTPie or curl).

The jdl definition of Attachment is:

/**
 * An entity to store attachments
 */
entity Attachment {
  /** The name of the attachment */
  fileName String required,
  /** the attachment */
  attachedFile Blob required
}

The JHipster generator constructs an Attachment.java and AttachmentService.java and an AttachmentResource.java which I have not modified at all.

Using the web front end the following statements are logged when a file is loaded:

2018-09-18 09:18:11.501 DEBUG 14151 --- [  XNIO-2 task-7] c.k.kapture.aop.logging.LoggingAspect    : Enter: com.kaleido.kapture.web.rest.AttachmentResource.createAttachment() with argument[s] = [Attachment{id=null, fileName='test', attachedFile='[B@396897d6', attachedFileContentType='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'}]
2018-09-18 09:18:11.502 DEBUG 14151 --- [  XNIO-2 task-7] c.k.kapture.web.rest.AttachmentResource  : REST request to save Attachment : Attachment{id=null, fileName='test', attachedFile='[B@396897d6', attachedFileContentType='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'}
2018-09-18 09:18:11.504 DEBUG 14151 --- [  XNIO-2 task-7] c.k.kapture.aop.logging.LoggingAspect    : Enter: com.kaleido.kapture.service.AttachmentService.save() with argument[s] = [Attachment{id=null, fileName='test', attachedFile='[B@396897d6', attachedFileContentType='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'}]
2018-09-18 09:18:11.518 DEBUG 14151 --- [  XNIO-2 task-7] c.k.kapture.service.AttachmentService    : Request to save Attachment : Attachment{id=null, fileName='test', attachedFile='[B@396897d6', attachedFileContentType='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'}
    Hibernate: call next value for hibernate_sequence
2018-09-18 09:18:11.562 DEBUG 14151 --- [  XNIO-2 task-7] o.j.core.metamodel.type.TypeFactory      : javersType of class com.kaleido.kapture.domain.Attachment inferred as EntityType
2018-09-18 09:18:11.564 DEBUG 14151 --- [  XNIO-2 task-7] o.j.core.metamodel.type.TypeFactory      : javersType of class java.lang.Long inferred as ValueType, it's used as id-property type
2018-09-18 09:18:11.597 DEBUG 14151 --- [  XNIO-2 task-7] o.j.core.metamodel.type.TypeFactory      : javersType of class [B spawned as ArrayType from prototype ArrayType{baseType:'class [Ljava.lang.Object;'}
2018-09-18 09:18:11.599 DEBUG 14151 --- [  XNIO-2 task-7] o.javers.core.graph.ObjectGraphBuilder   : live graph assembled, object nodes: 1, entities: 1, valueObjects: 0
2018-09-18 09:18:11.695  INFO 14151 --- [  XNIO-2 task-7] org.javers.core.Javers                   : Commit(id:1.0, snapshots:1, author:admin, changes - NewObject:1), done in 144 millis (diff:71, persist:73)
2018-09-18 09:18:12.096 DEBUG 14151 --- [  XNIO-2 task-7] c.k.kapture.aop.logging.LoggingAspect    : Exit: com.kaleido.kapture.service.AttachmentService.save() with result = Attachment{id=1001, fileName='test', attachedFile='[B@396897d6', attachedFileContentType='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'}
    Hibernate: insert into attachment (attached_file, attached_file_content_type, file_name, id) values (?, ?, ?, ?)
2018-09-18 09:18:12.104 DEBUG 14151 --- [  XNIO-2 task-7] c.k.kapture.aop.logging.LoggingAspect    : Exit: com.kaleido.kapture.web.rest.AttachmentResource.createAttachment() with result = <201 Created,Attachment{id=1001, fileName='test', attachedFile='[B@396897d6', attachedFileContentType='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'},{Location=[/api/attachments/1001], X-kaptureApp-alert=[A new attachment is created with identifier 1001], X-kaptureApp-params=[1001]}>

Using HTTPie I am able to authenticate and get a JWT token

http POST :8080/api/authenticate password=**** username=admin

However, it is not obvious how to POST the file:

http -v --form POST :8080/api/attachments/ \
    'Authorization:Bearer ***.***.***' \
    fileName=test.txt \
    [email protected] \
    attachedFileContentType=text/plain

results in:

POST /api/attachments/ HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Authorization: Bearer ****.****.****
Connection: keep-alive
Content-Length: 404
Content-Type: multipart/form-data; 
boundary=087aa239fc6f4eb197fa883424e2bdc3
Host: localhost:8080
User-Agent: HTTPie/0.9.9

--087aa239fc6f4eb197fa883424e2bdc3
Content-Disposition: form-data; name="fileName"

test.txt
--087aa239fc6f4eb197fa883424e2bdc3
Content-Disposition: form-data; name="attachedFileContentType"

text/plain
--087aa239fc6f4eb197fa883424e2bdc3
Content-Disposition: form-data; name="attachedFile"; 
filename="test.txt"
Content-Type: text/plain

test test test

--087aa239fc6f4eb197fa883424e2bdc3--

HTTP/1.1 415 Unsupported Media Type
Accept: application/octet-stream, text/plain, application/xml, text/xml, application/x-www-form-urlencoded, application/x-jackson-smile, application/*+xml, multipart/form-data, application/json, application/cbor, application/*+json, */*
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Connection: keep-alive
Content-Type: application/problem+json
Date: Tue, 18 Sep 2018 14:27:08 GMT
Expires: 0
Pragma: no-cache
Transfer-Encoding: chunked
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block

{
    "detail": "Content type 'multipart/form-data;boundary=087aa239fc6f4eb197fa883424e2bdc3;charset=UTF-8' not supported",
    "message": "error.http.415",
    "path": "/api/attachments/",
    "status": 415,
    "title": "Unsupported Media Type",
    "type": "https://www.jhipster.tech/problem/problem-with-message"
}

How should I construct the POST?

Update

When I view what is happening in Chrome developer view I see that the request payload is

{"fileName":"test.txt","attachedFileContentType":"text/plain","attachedFile":"dGVzdCB0ZXN0IHRlc3QK"}

It's not clear where the attachedFile value is coming from?

The request headers generated by the Web UI are:

POST /api/attachments HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Content-Length: 100
Pragma: no-cache
Cache-Control: no-cache
Accept: application/json, text/plain, */*
Origin: http://localhost:8080
X-XSRF-TOKEN: *****************
Authorization: Bearer ****.****.****.****
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) 
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36
Content-Type: application/json
Referer: http://localhost:8080/
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Cookie: 
    access_token=****.****.****; session_token=****; XSRF-TOKEN=*****; Idea-1c443776=2050c190-4f64-4a09-a50c-a0ef97b6a9da; io=p4GYHllrvXuakjfbAAAA
1

There are 1 best solutions below

0
Mark On BEST ANSWER

The solution is to Base64 encode the file and provide that string as the value of attachedFile (Thanks Jon Ruddlell for the tip).

The following call will create a record with an attachment called test.txt with the base64 encoded content "test test test" and with text/plain as the content type.

http POST :8080/api/attachments/ \
  'Authorization:Bearer ****.****.****' \
  fileName=test.txt \
  attachedFile=dGVzdCB0ZXN0IHRlc3QK \
  attachedFileContentType=text/plain

For any real file it is not realistic to include the Base64 encoding of the file on the command line. Therefore you should Base64 encode the contents to a file and then reference the file with http thereby making it part of the JSON body.

Encode a file (schema.png) and write the encoding to another file (schema.png.base64):

openssl base64 -in schema.png > schema.png.base64

POST the file with the name schema2.png and the media type image/png

http POST :8080/api/attachments/ \  
  'Authorization:Bearer ****.****.***' \
   fileName=schema2.png \
   [email protected] \
   attachedFileContentType=image/png