How to Unit Test handling of incoming Jersey MultiPart requests - java

We have a REST service that accepts MultiPart POST requests containing BodyParts that hold InputStreams. Inside the REST service a file might be created based on the provided data.
Task
We want to unit test the class that does the file operations based on its MultiPart input. Note: Wo do NOT want to use Jersey-Test! Grizzly does not load our spring application context which we need to inject DAO and fileHandler services into our REST service class. We explicitly want to test how our fileHandler service processes multiPart data.
The problem however is that the MultiPart that is sent out from the REST Client is not the same as the one received by the REST Server as jersey probably does something with the data to stream it or whatever. Trying to test (see below) the following setup will result in an
IllegalArgumentException [B cannot be cast to com.sun.jersey.multipart.BodyPartEntity
REST Client - sending a MultiPart
(just snippets, I omitted the obvious stuff):
byte[] bytes = FileManager.readImageFileToArray(completePath, fileType);
MultiPart multiPart = new MultiPart().
bodyPart(new BodyPart(bytes, MediaType.APPLICATION_OCTET_STREAM_TYPE)).
bodyPart(new BodyPart(fileName, MediaType.APPLICATION_XML_TYPE)).
bodyPart(new BodyPart(senderId, MediaType.APPLICATION_XML_TYPE));
ClientConfig cc = new DefaultClientConfig();
cc.getClasses().add(MultiPartWriter.class);
Client client = Client.create(cc);
WebResource webResource = client.resource(requestUrl);
Builder builder = webResource.type(MediaType.MULTIPART_FORM_DATA_TYPE);
builder = addHeaderParams(builder, headerParams);
ClientResponse response = builder.post(ClientResponse.class, multiPart);
Server Side - receiving a MultiPart
REST:
#POST
#Consumes(MediaType.MULTIPART_FORM_DATA)
#Produces(MediaType.APPLICATION_JSON)
#Transactional
public Response create(MultiPart multiPart) {
try {
multiPartReader.saveFile(multiPart);
Server Side MultiPartReader to save file from multipart
public class MultiPartReader {
public void saveFile(MultiPart multiPart) throws IOException {
BodyPartEntity bpe = (BodyPartEntity) multiPart.getBodyParts().get(0).getEntity();
InputStream inputStream = bpe.getInputStream();
// ...
BufferedImage bi = ImageIO.read(inputStream);
String fileName = getFileNameFromMultiPart(multiPart);
File file = new File(filename);
if (file.isDirectory()) {
ImageIO.write(bi, formatName, file);
} else {
file.mkdirs();
ImageIO.write(bi, formatName, file);
}
bpe.close();
}
Test - handling an incoming MultiPart in isolation
Now I want to test the MultiPartReader:
#Test
public void saveFile_should_Create_file() throws IOException {
byte[] bytes = IOUtils.toByteArray(this.getClass().getResourceAsStream(fileResource));
MultiPart multiPart = new MultiPart().
bodyPart(new BodyPart(bytes, MediaType.APPLICATION_OCTET_STREAM_TYPE)).
bodyPart(new BodyPart(fileName, MediaType.APPLICATION_XML_TYPE)).
bodyPart(new BodyPart(senderId, MediaType.APPLICATION_XML_TYPE));
multiPartReader.saveFile(multiPart);
file = new File(fileName);
Assert.assertNotNull(file);
Assert.assertTrue(file.getTotalSpace() > 0);
file.delete();
}
But, like I said I get a
IllegalArgumentException [B cannot be cast to com.sun.jersey.multipart.BodyPartEntity
at
BodyPartEntity bpe = (BodyPartEntity) multiPart.getBodyParts().get(0).getEntity();
So what can I do to emulate the send/receive handled by jersey so that my test will get the same data as my REST service does deployed on a server and requested by a REST client?
EDIT
Using
BodyPartEntity bpe = multiPart.getBodyParts().get(0).getEntityAs(BodyPartEntity.class);
will throw a
IllegalStateException: Entity instance does not contain the unconverted content
Further pointer, I think, towards having to convert the test-generated MultiPart in some way, before calling my MultiPartReader..
There has to be some method in jersey, I can call that will do this converting just the way it does, when it sends out a MultiPart request on a deployed system or maybe it is the receiving end that does some parsing when receiving the HTTP request..?

Looking at the jersey-multipart docs I see:
"It is not currently possible to know ahead of time what Java class the application would prefer to use for each individual body part, so an appropriate Provider cannot be selected. Currently, the unparsed content of each body part is returned (as a byte array) in the entity property of the returned BodyPart} instance, and the application can decide what further steps are needed based on the headers included in that body part. The simplest technique is to examine the received BodyPart, and then call the getEntityAs() method once you know which implementation class you would prefer."
It looks like you need to follow that suggestion. Examine the byte array returned in your Server Side MultiPartReader code:
multiPart.getBodyParts().get(0).getEntity();
...and call getEntityAs() on the BodyPart.

Related

How to return both JSON response and Byte Array of file in Springboot RestController Response while downloading a file

I am developing a rest controller to download a .docx file into the client system. My code is working fine as the file is getting downloaded. Now I want to enhance the response. My requirement is to also send a JSON payload in the response along with the .docx file content, something like
{"message":"Report downloaded Successfully"}
incase of successful download or with a different message incase of failure.
Below is my restcontroller code:
#RestController
public class DownloadController {
#PostMapping(value="/download",
consumes = {"multipart/form-data"},
produces = {"application/octet-stream"})
public ResponseEntity<?> downloadFile(#RequestParam("file") MultipartFile uploadFile){
//business logic to create the attachment file
try {
File file = new File("path_to_.DOCX file_I_have_created");
byte[] contents = Files.readAllBytes(Paths.get(file.getAbsolutePath()));
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setContentDisposition(ContentDisposition.attachment().filename("survey.docx").build());
return new ResponseEntity<>(contents, headers, HttpStatus.OK);
} catch (Exception e){
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}
How do I modify my response code to send both the JSON message and the byte[] contents so that the file gets downloaded and I can see the JSON message in the response preview tab in the chrome or response body in postman?
UPDATE: I tried to define a response class like below
public class Downloadresponse {
private byte[] content;
private String message;
//getter,setters
}
With this change in place, I am getting below exception:
Resolved [ HttpMessageNotWritableException: No converter for [class ...Downloadresponse] with preset content-type "application/octet-stream”]
You can't. HTTP doesn't allow you to defined multiple content-types on 1 request/response. That being said, you could send the byte array base64 encoded as part of a json response but would need to handle it in the front-end (if you have any) as it would not trigger the file download process of the browser.
You can define custom class which hold your current content and message . So you can return that class in the response
ResponseClass
{
byte[] contents;
String message;
}
You can send a json object that contain the message and the file as encoded64 string.
And in the client side you decode it and download it.
public record MyRecord(String message, String encodedStringBase64, String filename) {}
...
try {
File file = new File("path_to_.DOCX file_I_have_created");
byte[] contents = Files.readAllBytes(Paths.get(file.getAbsolutePath()));
String encodedString = Base64.getEncoder().encodeToString(contents);
MyRecord record = new MyRecord("Report downloaded Successfully", encodedString, file.getName());
return log.traceExit(ResponseEntity.ok().headers(headers)
.contentType(MediaType.valueOf("application/json")).body(record));
} ...

How to set #PartFilename of Entity field in #POST RESTEasy client method in runtime?

I am trying to write Resteasy Client Class to make simple POST multipart/form-data request to upload file. So POST request must contain Content-Disposition: form-data; name="files"; filename="myfile.txt" parameters.
I am able to set this statically with #PartFilename annotation for field in entity (value) class. like this
public class UploadStreamMultipartBody {
#FormParam("files")
#PartFilename(value = "myfile.txt")
#PartType(MediaType.APPLICATION_OCTET_STREAM)
public InputStream file;
}
But I cannot realize how to make it dynamically so I can provide file name in RUNTIME 🤔
Cause annotation value is read once in compile time.
Use MultipartFormDataOutput instead.
Interface example:
#POST
#Path("/document-store")
#Consumes(MediaType.MULTIPART_FORM_DATA)
void uploadDocument(#MultipartForm MultipartFormDataOutput data);
Usage:
var form = new MultipartFormDataOutput();
form.addFormData(/*key=*/"file", inputStream, new MediaType("application", "pdf"), fileName);
documentStoreClient.uploadDocument(form);

Browsers do not automatically display an inline file returned from a back-end java HTTP response

I created a pdf on the fly with pdfbox, did corresponding tests and it is well formatted, I can save it, send it by email - and everything is working as expected.
Now that same pdf(without saving it), I returns it as a client hit a button; the respective request/response succeeds but the browser (any) do not display it.
Some context:
angularJS 1.6 on the front-end
jersey 1.9 as a rest api
HTTP POST method
No errors
It just stays on the current page
My code
final ByteArrayOutputStream pdfStream = (ByteArrayOutputStream) generatePricePDF((Price) services.includeClient(price), null);
StreamingOutput streamingOutput = new StreamingOutput() {
#Override
public void write(OutputStream outputStream) throws IOException, WebApplicationException {
pdfStream.writeTo(outputStream);
pdfStream.flush();
pdfStream.close();
}
};
return Response.ok(streamingOutput, MediaType.valueOf("application/pdf"))
.header("Content-Length", pdfStream.toString().length())
.header("Content-Disposition", "inline; filename=price").build();
}
As I mentioned it above, I can copy the encoded response:
JVBERi0xLjQKJfbk/N8KMSAwIG9iago8PAovVHlwZSAvQ2F0YWxvZwovVmVyc2lvbiAvMS40Ci9QYWdlcyAyIDAgUgo+PgplbmRvYmoKMiAwIG9iago8PAovVHlwZSAvUGFnZXMKL0tpZHMgWzMgMCBSXQovQ291bnQgMQo+PgplbmRvYmoKMyAwIG9iago8PAovVHlwZSAvUGFnZQovTWVkaWFCb3ggWzAuMCAwLjAgNjEyLjAgNzkyLjBdCi9QYXJlbnQgMiAwIFIKL0NvbnRlbnRzIDQgMCBSCi9SZXNvdXJjZXMgNSAwIFIKPj4KZW5kb2JqCjQgMCBvYmoKPDwKL0xlbmd0aCA0MzEKL0ZpbHRlciAvRmxhdGVEZWNvZGUKPj4Kc3RyZWFtDQp4nO1Xy27bMBC86yv2mByUcvmUiiBA7Ui3nqofIFQ6UKFaqCQDSb++I1J20cfFQG4W4MNydrg7JAcLa9dkH2omltQcMsOCnET4Nbszpryn5luWq9JQbhK4H1qft0PvY0oZQSLi9djRZz+SkMTFRyHwo/2XhqTgYq0Cbs5FZH96OXU92Ds/j2FKpZxcS2nFVjPnLuJVk+2azBgyLqWZTUxok/iP2jjltGUpLCIpnLFVXOllbVHYlk5YZcta2Qq4A8LgIA92mZhWgi0r7MMeRECtrcF8tuyWPEQtVVGJl/1Pi4blREmEY7RFObRZiXZfPaOJiXFl6yivQN6c8yivkuil6CIwIpB43ZH+kvI+50N05ZH+lHHXDxOFPrTzOIRjGw6+H15Gf+imefUOHlBdnvRBiIgau7qAlZQX9LcJ5GaCmzeBvkyCYjPBLZgAo/9fE2yT4LZM8L9JoLb/BJsJ1DYJNhPIbRJsJpA3MwmeznfEV90RS+zQKn3Jd+E4DzSFbqLvXU9tWrcnPyLw9Abk2A704xTmn74PeK9X387D9LB+rzOdrxvSNDqdG/0CJBiXHA0KZW5kc3RyZWFtCmVuZG9iago1IDAgb2JqCjw8Ci9Gb250IDYgMCBSCj4+CmVuZG9iago2IDAgb2JqCjw8Ci9GMSA3IDAgUgo+PgplbmRvYmoKNyAwIG9iago8PAovVHlwZSAvRm9udAovU3VidHlwZSAvVHlwZTEKL0Jhc2VGb250IC9UaW1lcy1Sb21hbgovRW5jb2RpbmcgL1dpbkFuc2lFbmNvZGluZwo+PgplbmRvYmoKeHJlZgowIDgKMDAwMDAwMDAwMCA2NTUzNSBmDQowMDAwMDAwMDE1IDAwMDAwIG4NCjAwMDAwMDAwNzggMDAwMDAgbg0KMDAwMDAwMDEzNSAwMDAwMCBuDQowMDAwMDAwMjQ3IDAwMDAwIG4NCjAwMDAwMDA3NTIgMDAwMDAgbg0KMDAwMDAwMDc4NSAwMDAwMCBuDQowMDAwMDAwODE2IDAwMDAwIG4NCnRyYWlsZXIKPDwKL1Jvb3QgMSAwIFIKL0lEIFs8RkI2MUI4MDM5Q0RFQzU0OTkwNDFFMENBMDc0RTNDREU+IDxGQjYxQjgwMzlDREVDNTQ5OTA0MUUwQ0EwNzRFM0NERT5dCi9TaXplIDgKPj4Kc3RhcnQ=
and I can convert it (using an online converted), So there it is my pdf.
I have reviewed Content-Disposition
and it seems that the header are ok.
I was not managing the response accordingly, this is the code added in the AngularJs 1.x Controller:
$http.post('rest/processprice', $scope.price, {responseType: "blob"})
.then(function(response) {
var file = new Blob([response.data], {type: "application/pdf"});
var fileURL = URL.createObjectURL(file);
$window.open(fileURL, '_blank');
});
Managing the response this way, the pdf file opens up automatically.

Upload a file to a rest server using apache camel

I need to sync a file from a folder to a rest endpoint. So if a file is placed in a specific folder I need to send that file to a REST endpoint accepting multipart files. I am using apache camel to achieve this.
The REST endpoint is written in Spring and is as below:
#PostMapping("/")
public String handleFileUpload(#RequestParam("file") MultipartFile file,
RedirectAttributes redirectAttributes) {
storageService.store(file);
redirectAttributes.addFlashAttribute("message",
"You successfully uploaded " + file.getOriginalFilename() + "!");
return "redirect:/";
}
I am new to Camel and have figured out how to poll the directory by building a route and fetch the file, but I am unable to figure out how this route has to be used to put this file to the rest endpoint. This is what I have tried:
#Component
public class SampleCamelRouter extends RouteBuilder {
#Override
public void configure() throws Exception {
from("file://target/input?delay=4000")
.process(new Processor() {
public void process(Exchange exchange) throws Exception {
File filetoUpload = exchange.getIn().getBody(File.class);
String fileName = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
MultipartEntityBuilder entity = MultipartEntityBuilder.create();
entity.addTextBody("fileName", fileName);
entity.addBinaryBody("file", filetoUpload);
ByteArrayOutputStream out = new ByteArrayOutputStream();
entity.build().writeTo(out);
InputStream inputStream = new ByteArrayInputStream(out.toByteArray());
exchange.getIn().setBody(inputStream);
}
})
.setHeader(Exchange.CONTENT_TYPE, constant("multipart/form-data"))
.setHeader(Exchange.HTTP_METHOD, constant("POST"))
.to("http://localhost:9090/");
}
}
But when I run this, I get the below exception in the rest server:
org.apache.tomcat.util.http.fileupload.FileUploadException: the request was rejected because no multipart boundary was found
And on the camel side the request fails with a 500 error.
Any leads are appreciated.
use headers = "Content-Type= multipart/form-data", into controller.
The problem isn't in your code - it's in your request. You're missing boundary in your multipart request. As it said in specification here:
The Content-Type field for multipart entities requires one parameter, "boundary", which is used to specify the encapsulation boundary. The encapsulation boundary is defined as a line consisting entirely of two hyphen characters ("-", decimal code 45) followed by the boundary parameter value from the Content-Type header field.
This and this posts should also be helpful.

File upload along with other object in Jersey restful web service

I want to create an employee information in the system by uploading an image along with employee data. I am able to do it with different rest calls using jersey. But I want to achieve in one rest call.
I provide below the structure. Please help me how to do in this regard.
#POST
#Path("/upload2")
#Consumes({MediaType.MULTIPART_FORM_DATA,MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response uploadFileWithData(
#FormDataParam("file") InputStream fileInputStream,
#FormDataParam("file") FormDataContentDisposition contentDispositionHeader,
Employee emp) {
//..... business login
}
Whenever I am trying to do, I get error in Chrome postman. The simple structure of my Employee json is given below.
{
"Name": "John",
"Age": 23,
"Email": "john#gmail.com",
"Adrs": {
"DoorNo": "12-A",
"Street": "Street-11",
"City": "Bangalore",
"Country": "Karnataka"
}
}
However I can do it by making two different call, but I want to achieve in one rest call so that I can receive the file as well as the actual data of the employee.
Request you to help in this regard.
You can't have two Content-Types (well technically that's what we're doing below, but they are separated with each part of the multipart, but the main type is multipart). That's basically what you are expecting with your method. You are expecting mutlipart and json together as the main media type. The Employee data needs to be part of the multipart. So you can add a #FormDataParam("emp") for the Employee.
#FormDataParam("emp") Employee emp) { ...
Here's the class I used for testing
#Path("/multipart")
public class MultipartResource {
#POST
#Path("/upload2")
#Consumes({MediaType.MULTIPART_FORM_DATA})
public Response uploadFileWithData(
#FormDataParam("file") InputStream fileInputStream,
#FormDataParam("file") FormDataContentDisposition cdh,
#FormDataParam("emp") Employee emp) throws Exception{
Image img = ImageIO.read(fileInputStream);
JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(img)));
System.out.println(cdh.getName());
System.out.println(emp);
return Response.ok("Cool Tools!").build();
}
}
First I just tested with the client API to make sure it works
#Test
public void testGetIt() throws Exception {
final Client client = ClientBuilder.newBuilder()
.register(MultiPartFeature.class)
.build();
WebTarget t = client.target(Main.BASE_URI).path("multipart").path("upload2");
FileDataBodyPart filePart = new FileDataBodyPart("file",
new File("stackoverflow.png"));
// UPDATE: just tested again, and the below code is not needed.
// It's redundant. Using the FileDataBodyPart already sets the
// Content-Disposition information
filePart.setContentDisposition(
FormDataContentDisposition.name("file")
.fileName("stackoverflow.png").build());
String empPartJson
= "{"
+ " \"id\": 1234,"
+ " \"name\": \"Peeskillet\""
+ "}";
MultiPart multipartEntity = new FormDataMultiPart()
.field("emp", empPartJson, MediaType.APPLICATION_JSON_TYPE)
.bodyPart(filePart);
Response response = t.request().post(
Entity.entity(multipartEntity, multipartEntity.getMediaType()));
System.out.println(response.getStatus());
System.out.println(response.readEntity(String.class));
response.close();
}
I just created a simple Employee class with an id and name field for testing. This works perfectly fine. It shows the image, prints the content disposition, and prints the Employee object.
I'm not too familiar with Postman, so I saved that testing for last :-)
It appears to work fine also, as you can see the response "Cool Tools". But if we look at the printed Employee data, we'll see that it's null. Which is weird because with the client API it worked fine.
If we look at the Preview window, we'll see the problem
There's no Content-Type header for the emp body part. You can see in the client API I explicitly set it
MultiPart multipartEntity = new FormDataMultiPart()
.field("emp", empPartJson, MediaType.APPLICATION_JSON_TYPE)
.bodyPart(filePart);
So I guess this is really only part of a full answer. Like I said, I am not familiar with Postman So I don't know how to set Content-Types for individual body parts. The image/png for the image was automatically set for me for the image part (I guess it was just determined by the file extension). If you can figure this out, then the problem should be solved. Please, if you find out how to do this, post it as an answer.
See UPDATE below for solution
And just for completeness...
See here for more about MultiPart with Jersey.
Basic configurations:
Dependency:
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-multipart</artifactId>
<version>${jersey2.version}</version>
</dependency>
Client config:
final Client client = ClientBuilder.newBuilder()
.register(MultiPartFeature.class)
.build();
Server config:
// Create JAX-RS application.
final Application application = new ResourceConfig()
.packages("org.glassfish.jersey.examples.multipart")
.register(MultiPartFeature.class);
If you're having problems with the server configuration, one of the following posts might help
What exactly is the ResourceConfig class in Jersey 2?
152 MULTIPART_FORM_DATA: No injection source found for a parameter of type public javax.ws.rs.core.Response
UPDATE
So as you can see from the Postman client, some clients are unable to set individual parts' Content-Type, this includes the browser, in regards to it's default capabilities when using FormData (js).
We can't expect the client to find away around this, so what we can do, is when receiving the data, explicitly set the Content-Type before deserializing. For example
#POST
#Path("upload2")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFileAndJSON(#FormDataParam("emp") FormDataBodyPart jsonPart,
#FormDataParam("file") FormDataBodyPart bodyPart) {
jsonPart.setMediaType(MediaType.APPLICATION_JSON_TYPE);
Employee emp = jsonPart.getValueAs(Employee.class);
}
It's a little extra work to get the POJO, but it is a better solution than forcing the client to try and find it's own solution.
Another option is to use a String parameter and use whatever JSON library you use to deserialze the String to the POJO (like Jackson ObjectMapper). With the previous option, we just let Jersey handle the deserialization, and it will use the same JSON library it uses for all the other JSON endpoints (which might be preferred).
Asides
There is a conversation in these comments that you may be interested in if you are using a different Connector than the default HttpUrlConnection.
You can access the Image File and data from a form using MULTIPART FORM DATA By using the below code.
#POST
#Path("/UpdateProfile")
#Consumes(value={MediaType.APPLICATION_JSON,MediaType.MULTIPART_FORM_DATA})
#Produces(value={MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
public Response updateProfile(
#FormDataParam("file") InputStream fileInputStream,
#FormDataParam("file") FormDataContentDisposition contentDispositionHeader,
#FormDataParam("ProfileInfo") String ProfileInfo,
#FormDataParam("registrationId") String registrationId) {
String filePath= "/filepath/"+contentDispositionHeader.getFileName();
OutputStream outputStream = null;
try {
int read = 0;
byte[] bytes = new byte[1024];
outputStream = new FileOutputStream(new File(filePath));
while ((read = fileInputStream.read(bytes)) != -1) {
outputStream.write(bytes, 0, read);
}
outputStream.flush();
outputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch(Exception ex) {}
}
}
}
When I tried #PaulSamsotha's solution with Jersey client 2.21.1, there was 400 error. It worked when I added following in my client code:
MediaType contentType = MediaType.MULTIPART_FORM_DATA_TYPE;
contentType = Boundary.addBoundary(contentType);
Response response = t.request()
.post(Entity.entity(multipartEntity, contentType));
instead of hardcoded MediaType.MULTIPART_FORM_DATA in POST request call.
The reason this is needed is because when you use a different Connector (like Apache) for the Jersey Client, it is unable to alter outbound headers, which is required to add a boundary to the Content-Type. This limitation is explained in the Jersey Client docs. So if you want to use a different Connector, then you need to manually create the boundary.
Your ApplicationConfig should register the MultiPartFeature.class from the glassfish.jersey.media.. so as to enable file upload
#javax.ws.rs.ApplicationPath(ResourcePath.API_ROOT)
public class ApplicationConfig extends ResourceConfig {
public ApplicationConfig() {
//register the necessary headers files needed from client
register(CORSConfigurationFilter.class);
//The jackson feature and provider is used for object serialization
//between client and server objects in to a json
register(JacksonFeature.class);
register(JacksonProvider.class);
//Glassfish multipart file uploader feature
register(MultiPartFeature.class);
//inject and registered all resources class using the package
//not to be tempered with
packages("com.flexisaf.safhrms.client.resources");
register(RESTRequestFilter.class);
}
I used file upload example from,
http://www.mkyong.com/webservices/jax-rs/file-upload-example-in-jersey/
in my resource class i have below method
#POST
#Path("/upload")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response attachupload(#FormDataParam("file") byte[] is,
#FormDataParam("file") FormDataContentDisposition fileDetail,
#FormDataParam("fileName") String flename){
attachService.saveAttachment(flename,is);
}
in my attachService.java i have below method
public void saveAttachment(String flename, byte[] is) {
// TODO Auto-generated method stub
attachmentDao.saveAttachment(flename,is);
}
in Dao i have
attach.setData(is);
attach.setFileName(flename);
in my HBM mapping is like
<property name="data" type="binary" >
<column name="data" />
</property>
This working for all type of files like .PDF,.TXT, .PNG etc.,
The request type is multipart/form-data and what you are sending is essentially form fields that go out as bytes with content boundaries separating different form fields.To send an object representation as form field (string), you can send a serialized form from the client that you can then deserialize on the server.
After all no programming environment object is actually ever traveling on the wire. The programming environment on both side are just doing automatic serialization and deserialization that you can also do. That is the cleanest and programming environment quirks free way to do it.
As an example, here is a javascript client posting to a Jersey example service,
submitFile(){
let data = new FormData();
let account = {
"name": "test account",
"location": "Bangalore"
}
data.append('file', this.file);
data.append("accountKey", "44c85e59-afed-4fb2-884d-b3d85b051c44");
data.append("device", "test001");
data.append("account", JSON.stringify(account));
let url = "http://localhost:9090/sensordb/test/file/multipart/upload";
let config = {
headers: {
'Content-Type': 'multipart/form-data'
}
}
axios.post(url, data, config).then(function(data){
console.log('SUCCESS!!');
console.log(data.data);
}).catch(function(){
console.log('FAILURE!!');
});
},
Here the client is sending a file, 2 form fields (strings) and an account object that has been stringified for transport. here is how the form fields look on the wire,
On the server, you can just deserialize the form fields the way you see fit. To finish this trivial example,
#POST
#Path("/file/multipart/upload")
#Consumes({MediaType.MULTIPART_FORM_DATA})
public Response uploadMultiPart(#Context ContainerRequestContext requestContext,
#FormDataParam("file") InputStream fileInputStream,
#FormDataParam("file") FormDataContentDisposition cdh,
#FormDataParam("accountKey") String accountKey,
#FormDataParam("account") String json) {
System.out.println(cdh.getFileName());
System.out.println(cdh.getName());
System.out.println(accountKey);
try {
Account account = Account.deserialize(json);
System.out.println(account.getLocation());
System.out.println(account.getName());
} catch (Exception e) {
e.printStackTrace();
}
return Response.ok().build();
}

Categories