Spring boot Multipart file upload using Client Side Java Code - java

I have written a restful web service in spring boot which receives the file.
#RequestMapping(value = "/upload", method = RequestMethod.POST)
#ResponseBody
public void uploadFile(#RequestParam("file") MultipartFile uploadfile) {
System.out.println("filename: " + uploadfile.getName());
}
How can we upload the file from client side java code to web service. Instead of AJAX call or HTML page form multipart request?
The code below call the web service with JSON object. Like this I want to receive the file in above written web service.
void clientRequest(String server_url, JSONObject fileObj){
try {
URL url = new URL(server_url);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setDoOutput(true);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/json");
OutputStream os = conn.getOutputStream();
os.write(fileObj.toString().getBytes());
os.flush();
BufferedReader br = new BufferedReader(new InputStreamReader(
(conn.getInputStream())));
String output;
System.out.println("Output from Server .... \n");
while ((output = br.readLine()) != null) {
logger.info("output :: " + output);
}
conn.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}

You can use Spring's HttpEntity along with ByteArrayResource to upload the file, here is an example:
MultiValueMap<String, Object> data = new LinkedMultiValueMap<String, Object>();
ByteArrayResource resource = new ByteArrayResource(file.getBytes()) {
#Override
public String getFilename() {
return file.getName();
}
};
data.add("file", resource);
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<MultiValueMap<String, Object>>(data, requestHeaders);
final ResponseEntity<<SomeClass>> responseEntity = restTemplate.exchange(<url>,
HttpMethod.POST, requestEntity, new ParameterizedTypeReference<SomeClass>(){});
SomeClass result = responseEntity.getBody();

If you want to use a MultipartFile, you must use the multipart/form-data mimetype when requesting. Instead of sending the JSON as request entity, you should construct a specific multipart-entity with a single field file in it.
This is how it's done: How can I make a multipart/form-data POST request using Java?

Related

Send JSON body in HTTP GET request in java/spring boot

I need to send a GET request with a json body in java/spring boot. I'm aware of the advice against it, however I have to do it this was for a couple of reasons:
1. The 3rd party API I'm using only allows GET requests, so POST is not an option.
2. I need to pass an extremely large parameter in the body (a comma separated list of about 8-10k characters) so tacking query params onto the url is not an option either.
I've tried a few different things:
apache HttpClient from here: Send content body with HTTP GET Request in Java. This gave some error straight from the API itself about a bad key.
URIComponentsBuilder from here: Spring RestTemplate GET with parameters. This just tacked the params onto the url, which as I explained before is not an option.
restTemplate.exchange. This seemed the most straightforward, but the object wouldn't pass: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html#exchange-java.lang.String-org.springframework.http.HttpMethod-org.springframework.http.HttpEntity-java.lang.Class-java.util.Map-
as well as probably another thing or two that I've forgotten about.
Here is what I'm talking about in Postman. I need to be able to pass both of the parameters given here. It works fine if run through Postman, but I can't figure it out in Java/Spring Boot.
Here is a code snippet from the restTemplate.exchange attempt:
public String makeMMSICall(String uri, List<String> MMSIBatchList, HashMap<String, String> headersList) {
ResponseEntity<String> result = null;
try {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
for (String key : headersList.keySet()) {
headers.add(key, headersList.get(key));
}
Map<String, String> params = new HashMap<String, String>();
params.put("mmsi", String.join(",", MMSIBatchList));
params.put("limit", mmsiBatchSize);
HttpEntity<?> entity = new HttpEntity<>(headers);
result = restTemplate.exchange(uri, HttpMethod.GET, entity, String.class, params);
System.out.println(result.getBody());
} catch (RestClientException e) {
LOGGER.error("Exception in makeGetHTTPCall :" + e.getMessage());
throw e;
} catch (Exception e) {
LOGGER.error("Exception in makeGetHTTPCall :" + e.getMessage());
throw e;
}
return result.getBody();
}
Thanks for helping!
You can try java.net.HttpUrlConnection, it works for me but indeed I normally use a POST
HttpURLConnection connection = null;
BufferedReader reader = null;
String payload = "body";
try {
URL url = new URL("url endpoint");
if (url.getProtocol().equalsIgnoreCase("https")) {
connection = (HttpsURLConnection) url.openConnection();
} else {
connection = (HttpURLConnection) url.openConnection();
}
// Set connection properties
connection.setRequestMethod(method); // get or post
connection.setReadTimeout(3 * 1000);
connection.setDoOutput(true);
connection.setUseCaches(false);
if (payload != null) {
OutputStream os = connection.getOutputStream();
os.write(payload.getBytes(StandardCharsets.UTF_8));
os.flush();
os.close();
}
int responseCode = connection.getResponseCode();
}
There's no way of implementing it via RestTemplate, even with .exchange method. It'll simply not send the request body for GET calls even if we pass the entity within the function parameters.(Tested via interceptor logs)
You can use the Apache client to solve this issue/request (whatever you'd like to call it). The code you need is something along following lines.
private static class HttpGetWithBody extends HttpEntityEnclosingRequestBase {
JSONObject requestBody;
public HttpGetWithBody(URI uri, JSONObject requestBody) throws UnsupportedEncodingException {
this.setURI(uri);
StringEntity stringEntity = new StringEntity(requestBody.toString());
super.setEntity(stringEntity);
this.requestBody = requestBody;
}
#Override
public String getMethod() {
return "GET";
}
}
private JSONObject executeGetRequestWithBody(String host, Object entity) throws ClientProtocolException, IOException {
CloseableHttpClient httpClient = HttpClients.createDefault();
try{
JSONObject requestBody = new JSONObject(entity);
URL url = new URL(host);
HttpRequest request = new HttpGetWithBody(url.toURI(), requestBody);
request.addHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
request.addHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE);
HttpResponse response;
if(url.getPort() != 0) response = httpClient.execute(new HttpHost(url.getHost(), url.getPort()), request);
else response = httpClient.execute(new HttpHost(url.getHost()), request);
if(response.getStatusLine().getStatusCode() == HttpStatus.SC_OK){
JSONObject res = new JSONObject(EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8));
httpClient.close();
return res;
}
}catch (Exception e){
log.error("Error occurred in executeGetRequestWithBody. Error: ", e.getStackTrace());
}
httpClient.close();
return null;
}
If you inspect even Apache client library doesn't support passing the body natively(checked via code implementation of HttpGet method), since contextually request body for a GET request is not a good and obvious practice.
Try creating a new custom RequestFactory.
Similar to
get request with body

Simultaneously return a response and render a file for download to the browser using Spring Rest

I am receiving a post request from my vendor who expects an instant response (I am using Spring Rest). I use this request to validate some transactions and allow file download to the user on the browser. My question is how can i simultaneously render that file to the browser for download and return the expected response to my vendor. Here is my attempted solution (unsuccessful). Please help. With thanks.
#RequestMapping(method = {RequestMethod.GET,RequestMethod.POST}, value ="/listeningurl", consumes = "application/xml", produces = "application/xml")
public ResponseObject lodgementNotifications(#RequestBody RequesObject reqObject, HttpServletResponse response)
{
//do stuffs with reqObject;
//Generate a fileUrl using some parameters from the reqObject;
try {
//copies all bytes from a file to an output stream
URL fileURL = new URL(fileUrl);
HttpURLConnection httpConn = (HttpURLConnection) fileURL.openConnection();
InputStream inputStream = httpConn.getInputStream();
// set content type
response.setContentType(fileMetaData.getContentType());
// add response header
response.addHeader("Content-Disposition", "attachment; filename=" fileName"));
response.setContentLengthLong(fileMetaData.getContentLength());
response.setStatus(200);
FileCopyUtils.copy(inputStream, response.getOutputStream());
//flushes output stream
response.getOutputStream().flush();
} catch (IOException e) {
System.out.println("Error :- " + e.getMessage());
}
// Initialize ResponseObject
return responseObject
}

How to develop a spring rest web service in accessing external API?

I want to develop the spring rest web service which accesses the external API when user sends request. User sends a json request and set of request headers to the web service and web service should authenticate the user and call the eternal API. External API response should be given to the user as the response body. This is what I want to happen basically.
//Authenticate to access API
public class HotelbedsAuthentication {
final String hotelEndpoint = "https://api.test.hotelbeds.com/hotel-api/1.0/";
private String request;
private static final String apiKey="enter given api key for free";
private static final String secretKey="free key";
private String signature=org.apache.commons.codec.digest.DigestUtils.sha256Hex(apiKey + secretKey + System.currentTimeMillis() / 1000);
public HttpsURLConnection findHotels(){
HttpsURLConnection connection = null;
try{
URL url = new URL(hotelEndpoint+getRequest());
HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
con.setRequestProperty("X-Signature", signature);
con.setRequestProperty("Api-Key", apiKey);
con.setRequestProperty("Accept", "application/json");
con.setRequestProperty("Accept-Encoding", "gzip");
con.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
con.setRequestMethod("POST");
con.setDoOutput(true);
con.setDoInput (true);
connection=con;
}catch(Exception error ){
System.out.println("An error occured "+error);
}
return connection;
}
public String getRequest() {
return request;
}
public void setRequest(String request) {
this.request = request;
}
}
//Rest Controller
public class FindHotelController {
HotelbedsAuthentication token = new HotelbedsAuthentication();
#RequestMapping(value="hotels",method=RequestMethod.POST,produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public HotelAvailability findHotel(#RequestBody JSONObject request){
HttpsURLConnection connection;
File pathDir = new File("C:/Users/User/workspace/SpringRestSample/src/main/java");
JCodeModel codeModel = new JCodeModel();
JSONObject response= new JSONObject();
HotelAvailability sample= new HotelAvailability();
//String userRequest="{\"stay\": {\"checkIn\": \"2018-01-29\",\"checkOut\": \"2018-01-31\",\"shiftDays\": \"2\"},\"occupancies\": [{\"rooms\": 1,\"adults\": 2,\"children\": 1,\"paxes\": [{\"type\": \"AD\",\"age\": 30},{\"type\": \"AD\",\"age\": 30},{\"type\": \"CH\",\"age\": 8}]}],\"hotels\": {\"hotel\": [1067,1070,1075,135813,145214,1506,1508,1526,1533,1539,1550,161032,170542,182125,187939,212167,215417,228671,229318,23476]}}";
try{
token.setRequest("hotels");
connection= token.findHotels();
//Read request and embed to url
OutputStream os = connection.getOutputStream();
os.write(request.toString().getBytes("UTF-8"));
os.close();
// read the response
InputStream in = new BufferedInputStream(connection.getInputStream());
String result = org.apache.commons.io.IOUtils.toString(in, "UTF-8");
JSONObject jsonObject = new JSONObject(result);
response=jsonObject;
GenerationConfig config = new DefaultGenerationConfig() {
#Override
public boolean isGenerateBuilders() {
return true;
}
public SourceType getSourceType(){
return SourceType.JSON;
}
};
SchemaMapper mapper =new SchemaMapper(new RuleFactory(config, new GsonAnnotator(config), new SchemaStore()), new SchemaGenerator());
mapper.generate(codeModel, "HotelAvailability","com.sample.model",response.toString());
codeModel.build(pathDir);
ObjectMapper objectMapper = new ObjectMapper();
sample = objectMapper.readValue(response.toString(), HotelAvailability.class);
in.close();
connection.disconnect();
}catch(Exception ex){
System.out.println(ex);
}
System.out.println(sample);
return sample;
}
}
What you're looking for is an API Gateway or a simple router based on your requirements. If you need to make changes to the response, you need a Gateway. If you're looking to simply pass the request then you need a router.
There are many ways to do this, but as always, Spring has already built a tool for that. check out Zuul. This will allow you to integrate with an Authentication provider and then delegate requests to microservices.
Gateway
https://www.intertech.com/Blog/spring-integration-tutorial-part-8-gateways/
OAuth Provider
https://spring.io/guides/tutorials/spring-boot-oauth2/#_social_login_authserver
Router
https://cloud.spring.io/spring-cloud-netflix/multi/multi__router_and_filter_zuul.html

Server returned HTTP response code: 415 Unsupported media type

I have a rest web service like below.
#POST
#Path("/startProcess")
#Produces(MediaType.TEXT_PLAIN)
#Consumes(MediaType.APPLICATION_JSON)
public String startProcess(InputParams inputParams, #Context HttpServletRequest request, #Context HttpServletResponse response) {
ProjectBean projBean = new ProjectBean();
Helper.loadProjectBean(inputParams, projBean);
return "1";
}
Now I am trying to consume it with below main program.
public static void main(String[] args) throws Exception {
StringBuffer response = new StringBuffer();
String taigaServiceUrl = "http://localhost:8181/restServer/rest/TestWebService/startProcess/";
URL url = new URL(taigaServiceUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setDoOutput(true);
conn.setRequestProperty("Content-Type", "application/json");
String userpass = "admin" + ":" + "admin";
String basicAuth = "Basic " + new String(new Base64().encode(userpass.getBytes()));
conn.setRequestProperty("Authorization", basicAuth);
InputParams inputParams = new InputParams();
inputParams.setXXX("xxxx");
inputParams.setYYYY("123456");
inputParams.setZZZZ("ZZZZ");
String json = new Gson().toJson(inputParams);
DataOutputStream os = new DataOutputStream (conn.getOutputStream());
os.write(json.getBytes());
os.flush();
BufferedReader br = new BufferedReader(new InputStreamReader((conn.getInputStream())));
String inputLine;
while ((inputLine = br.readLine()) != null) {
response.append(inputLine);
}
br.close();
}
But every time I am getting below error.
Exception in thread "main" java.io.IOException: Server returned HTTP response code: 415 for URL: http://localhost:8181/restServer/rest/TestWebService/startProcess/
at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(Unknown Source)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source)
at scm.controllers.Test.main(Test.java:64)
As per error the media type is unsupported. In my rest webservice I am consuming JSON and in my main program I am sending JSON. Then where it is breaking?
Well after lot of debugging I found solution of my problem. I needed to add below jars in classpath. Actually Jersey was not able to bind JSON object to the rest service.
jackson-annotations-2.5.4.jar
jackson-core-2.5.4.jar
jackson-databind-2.5.4.jar
jackson-jaxrs-base-2.5.4.jar
jackson-jaxrs-json-provider-2.5.4.jar
jersey-entity-filtering-2.22.2.jar
jersey-media-json-jackson-2.22.2.jar
Have a look at this guide:
I think you need to define a json processor:
https://www.nabisoft.com/tutorials/java-ee/producing-and-consuming-json-or-xml-in-java-rest-services-with-jersey-and-jackson
thanks.
This is the issue with your #Produces and #Consumes.
#Produces(MediaType.TEXT_PLAIN)
#Consumes(MediaType.APPLICATION_JSON)
As per the annotation, your endpoint receives JSON and result would be TEXT.
But in your client program, you have mentioned content type as json.
conn.setRequestProperty("Content-Type", "application/json");
Hence client expects a json, where as its not.
Change this as
conn.setRequestProperty("Content-Type", "text/plain");
would work.

SpringMVC-FileUpload - The request sent by the client was syntactically incorrect

I've seen couple of qts on the same topic. But I didn't find any clue of this error.
I am working on a POC and following the link below.
http://spring.io/guides/gs/uploading-files/
As mentioned in the above tutorial, in standalone mode[spring embeded Tomcat] it is working absolutely fine.
But I want to deploy it as webapplication. So, I have created a separate SpringMVC project and added the following controller.
Controller file
#Controller
public class FileUploadController {
#RequestMapping(value="/upload", method=RequestMethod.GET)
public #ResponseBody String provideUploadInfo() {
return "You can upload a file by posting to this same URL.";
}
#RequestMapping(value="/upload", method=RequestMethod.POST)
public #ResponseBody String handleFileUpload(#RequestParam("name") String name,
#RequestParam("file") MultipartFile file){
if (!file.isEmpty()) {
try {
byte[] bytes = file.getBytes();
BufferedOutputStream stream =
new BufferedOutputStream(new FileOutputStream(new File(name + "-uploaded")));
stream.write(bytes);
stream.close();
return "You successfully uploaded " + name + " into " + name + "-uploaded !";
} catch (Exception e) {
return "You failed to upload " + name + " => " + e.getMessage();
}
} else {
return "You failed to upload " + name + " because the file was empty.";
}
}
}
I've written the following client ( As I don't want to use RestTemplate here).
Service Client
private static final String URL_GET = "http://localhost:8080/SpringMVC/upload";
static String URL = "http://localhost:8080/SpringMVC/upload";
public static void main(String[] args) throws Exception {
PropertyConfigurator.configure("C:/DevEnvProject/eclipse/workspace_exp/OCR/log4j.properties");
testGet();
testPOST();
}
private static void testGet() throws ClientProtocolException, IOException {
HttpClient httpClient = new DefaultHttpClient();
HttpContext localContext = new BasicHttpContext();
HttpGet httpGet = new HttpGet(URL_GET);
HttpResponse response = httpClient.execute(httpGet, localContext);
BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent(), "UTF-8"));
String sResponse = reader.readLine();
}
static void testPOST() {
try {
HttpClient httpClient = new DefaultHttpClient();
HttpContext localContext = new BasicHttpContext();
HttpPost httpPost = new HttpPost(URL);
MultipartEntity entity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE);
entity.addPart("name", new StringBody("testIcon.png"));
entity.addPart("file", new FileBody(new File("C:/testIcon.png")));
httpPost.setEntity(entity);
HttpResponse response = httpClient.execute(httpPost, localContext);
BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent(), "UTF-8"));
String sResponse = reader.readLine();
} catch (Exception e) {
e.printStackTrace();
}
}
I couldn't make a successful call to the POST endpoint. Everytime, I'm getting the following exception.
400 Bad Request - The request sent by the client was syntactically incorrect
'GET' call is working fine. I compared the the log of the 'POST' request with the same 'POST' request which I got while testing with standalone approach as mentioned in the spring tutorial. Didn't find any diff in the request part.
I know that I'm quite verbose in this post. I wanted to give as much context info as possible. Please help.
Thanks
There are 2 things you need to do:
First, add the Apache Commons FileUpload library to your class path. If you use maven, you can get the dependency here. If you don't, you can still download the jar and add it manually.
Second, you have to declare a MultipartResolver bean in your context with name multipartResolver. With Apache Commonds FileUpload, you can use CommonsMultipartResolver. For example, with Java config, that would be
#Bean(name = "multipartResolver")
public CommonsMultipartResolver multipartResolver() {
CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver();
// set any fields
return commonsMultipartResolver;
}
With XML config,
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- set any properties -->
</bean>
This is further documented in the Spring official documentation.

Categories