You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@camel.apache.org by jamalissimo <ro...@gmail.com> on 2013/03/06 13:51:32 UTC

Send file via POST request

Hello guys,

I am currently working on quite exciting assignment. Here is what I want to
do:

@GET REST response -> CXF to get parameters from exchange -> 3rd Party API
to get session_id -> BEAN to send data via POST method and in
multipart/mixed( it is for uploading files )

My first problem is that I don't know how to set the REST, for now it looks
like this:

@GET
@Path("/{myServerHost}/upload")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.MULTIPART_FORM_DATA)

public Response uploadFile(@PathParam("myHost") String knowledgetreeHost,
			@QueryParam("myPort") String knowledgetreePort, @QueryParam("user")
String user,
			@QueryParam("password") String password, @QueryParam("file") String file)
{
		return null;
}

Then I have route and I am setting http method to post

.when(header(CxfConstants.OPERATION_NAME).isEqualTo("uploadFile"))
 
.recipientList("https://myserver.com/service/?method=login&username=myusername&password=123456")
     .beanRef("myBean", "getSessionId")
      // For now I needed data in GET(session_id),it is no problem, but in
the following method
      // I need to send POST request 
       .setHeader(Exchange.HTTP_METHOD, constant("POST"))
        .beanRef("myBean","uploadMyFile")
.endChoice()

Method uploadMyFile should send POST request. For now it looks like this:

	public void uploadMyFile(Exchange exchange) throws IOException {
                //SESSION_ID is set in previous steps of route
		String sessionId = exchange.getProperty("SESSION_ID").toString();
               //FILENAME is taken from URL
               /**
                  String filename = (String) msgList.get(4);
		  exchange.setProperty("FILENAME", filename);
                */
		String filename = "@"+exchange.getProperty("FILENAME").toString();
		String pathToUploader = "https://myserver.com/service/upload.php";

		// Instantiate an HttpClient
		HttpClient client = new HttpClient();

		// Instantiate a POST HTTP method
		PostMethod method = new PostMethod(pathToUploader);
		method.setRequestHeader("Content-type",
exchange.getIn().getHeader("CONTENT_TYPE") + ";charset="+
exchange.getIn().getHeader("CONTENT_ENCODING"));

		// Define name-value pairs to set into the QueryString

		NameValuePair nvp1 = new NameValuePair("session_id", sessionId);
		NameValuePair nvp2 = new NameValuePair("action", "A");
		NameValuePair nvp3 = new NameValuePair("myfile", filename);
		
		method.setQueryString(new NameValuePair[] { nvp1, nvp2, nvp3 });

		try {
			int statusCode = client.executeMethod(method);

			System.out.println("Status Code = " + statusCode);
			System.out.println("QueryString>>> " + method.getQueryString());
			System.out.println("Status Text>>>" +
HttpStatus.getStatusText(statusCode));

			// Get data as a String
			System.out.println(method.getResponseBodyAsString());
			
			exchange.getIn().setHeader("HTTP_METHOD", "POST");
			exchange.getIn().setBody(method.getResponseBodyAsString());

			// release connection
			method.releaseConnection();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

Basically I need to know how to set REST and how to set POST method for CXF

Thanks

-Br, Roman



--
View this message in context: http://camel.465427.n5.nabble.com/Send-file-via-POST-request-tp5728674.html
Sent from the Camel - Users mailing list archive at Nabble.com.

Re: Send file via POST request

Posted by jamalissimo <ro...@gmail.com>.
This solution works ok with one little glitch. When I was working on my local
environment then there was no problem, but when I deployed it to my server I
noticed on little thing. The file I am uploading, has to be located on your
server, but you want to upload it from your(client's) drive. So you must
have two uploading parts. One which will move your file to your Karaf's
server, and the other one is that Camel will move it from its server to
wherever you want to upload the file.

It goes this way:

Your local drive e.g. /home/janulrom/Documents/test.txt -> Server where you
run Karaf e.g. /var/tmp/test.txt -> Server where you want to upload the file

it bugs me a lot, because I don't now how to pass file to route for example
as byte[] or so. The worst part is that you must have two upload parts which
can reduce memory when there is many requests. If someone has a hint how to
do that, it will be much appreciated.

-Br, Roman



--
View this message in context: http://camel.465427.n5.nabble.com/Send-file-via-POST-request-tp5728674p5729111.html
Sent from the Camel - Users mailing list archive at Nabble.com.

Re: Send file via POST request

Posted by jamalissimo <ro...@gmail.com>.
Hi guys,

I am back with the solution. I finally managed how to do that! First thing
was to find out that consumer sends all data in headers and you can't access
the exchange's body. I managed to get data from headers and pass them into
Camel's properties which could be then globally passed wherever I need to.

REST part
------------------------------------------------------------------------------------

	@POST
	@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
	@Path("/upload")
	@Produces(MediaType.APPLICATION_JSON)
	public Response uploadFile(MultipartBody multipartBody){
		return null;
	}

JAVA part
------------------------------------------------------------------------------------

/* 
 * Method for parsing CamelHttpQuery
 * The URL query looks like a=1&b=2
 */
public static Map<String, String> getQueryMap(String query) {
		String[] params = query.split("&");
		Map<String, String> map = new HashMap<String, String>();
		for (String param : params) {
			String name = param.split("=")[0];
			String value = param.split("=")[1];
			map.put(name, value);
		}
		return map;
	}

	/*
	 * Setting Camel's properties for @POST Consumer
	 * 
	 * MessageContentList can't be used, because @POST consumer sends all data
in headers.
	 * Therefore there is different way how to get data for properties.
	 */
	public void setupPostProperties(Exchange exchange) throws IOException {
		String query = exchange.getIn().getHeader("CamelHttpQuery").toString();
		Map<String, String> map = getQueryMap(query);

		String Host = map.get("Host");
		String Port = map.get("Port");
		String User = map.get("username");
		String Password = map.get("password");
		String Filename = map.get("pathToFile");
		String FolderId = map.get("folderId");

		exchange.setProperty("HOST", Host);
		exchange.setProperty("PORT", Port);
		exchange.setProperty("USERNAME", User);
		exchange.setProperty("PASSWORD", Password);
		exchange.setProperty("PATH_TO_FILE", Filename);
		exchange.setProperty("FOLDER_ID", FolderId);
		exchange.setProperty("UPLOAD_API", "/webservice/upload.php");

		logger.debug(String.format(
				"setupBasicProperties Host=%s, Port=%s, User=%s, "
						+ "Password=%s, Filename=%s, FolderId=%s",
				Host, Port, User, Password, Filename,
				FolderId));
	}

	/*
	 * Sending @POST HTTP request
	 * 
	 * Here we need org.apache.http.client.HttpClient library installed in
Karaf.
	 * It will not work with org.apache.commons.httpclient.HttpClient
         * You must install org.apache.httpcomponents to Karaf.
         * I am not sure which solution works, but one does :-)
         *
         * osgi:install -s wrap:mvn:org.apache.httpcomponents/httpmime/4.2.3
         * osgi:install -s
wrap:mvn:org.apache.httpcomponents/httpclient/4.2.3
         * osgi:install -s wrap:mvn:org.apache.httpcomponents/httpcore/4.2.3
         *
         * install mvn:org.apache.httpcomponents/httpmime/4.2.3
         * install mvn:org.apache.httpcomponents/httpclient/4.2.3
         * install mvn:org.apache.httpcomponents/httpcore-osgi/4.2.3

	 * 
	 * This method just sets variables which will be sent via @POST request
	 * Using MultipartEntity I can send variables in different format(binary
file and string in this case).
	 * If there is a need to add more parameters, just add one more
entity.addPart() 
	 */
	public void sendPostRequest(Exchange exchange) throws IOException {
		String pathToUploader = "https://" +
exchange.getProperty("HOST").toString() + ":" 
				+ exchange.getProperty("PORT").toString()
				+ exchange.getProperty("UPLOAD_API").toString();

		String filename = exchange.getProperty("PATH_TO_FILE").toString();
		String session_id = exchange.getProperty("SESSION_ID").toString();
		String action = "A";
		String output = "json";
		File myfile = new File(filename);
		
		if(myfile.exists()){

			HttpClient client = new DefaultHttpClient();
			client.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION,
HttpVersion.HTTP_1_1);
			HttpPost post = new HttpPost(pathToUploader);
	
			MultipartEntity entity = new
MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE);
	
			// For File parameters
			entity.addPart("myfile", new FileBody(((File) myfile),
"application/octet-stream"));
	
			// For usual String parameters
			entity.addPart("session_id", new StringBody(session_id, "text/plain",
Charset.forName("UTF-8")));
			entity.addPart("action", new StringBody(action, "text/plain",
Charset.forName("UTF-8")));
			entity.addPart("output", new StringBody(output, "text/plain",
Charset.forName("UTF-8")));
	
			post.setEntity(entity);
	
			String response = EntityUtils.toString(client.execute(post).getEntity(),
"UTF-8");
			exchange.getOut().setBody(response);
	
			client.getConnectionManager().shutdown();
		}else{
			logger.error("File does not exist!!!");
			throw new FileNotFoundException();		
		}

	}

CAMEL ROUTE
---------------------------------------------------------------------------------------------------

		from("cxfrs:bean:myServer")
		  .routeId("myService-mainRoute")
			.choice()		
			/*
			 * Upload part
			 */
			.when(header(Exchange.HTTP_PATH).isEqualTo("/upload"))
				/*
				 * Setting properties for @POST request
				 */
				.beanRef("myserviceBean", "setupPostProperties")
				/*
				 * Authentication and getting session_id
				 */
				.to("direct:getSessionId")
						/*
						 * Here you are telling Camel that you want to send data as @POST
						 */
						.setHeader(Exchange.HTTP_METHOD, constant("POST"))
					/*
					 * Here we send binary file and strings together as @POST HTTP request.
					 * It simulates sending of form via @POST method.
					 * Response is already in JSON format
					 */
					.beanRef("MyBean","sendPostRequest")

			.endChoice();

                /*
                 * Here you just call address where you get session_id
                 * based on your credentials.
                 */
		from("direct:getSessionId")
			.removeHeader(Exchange.HTTP_URI)
			.removeHeader(Exchange.HTTP_PATH)
			.removeHeader(Exchange.HTTP_QUERY)		
				.recipientList(simple(BASE + API + "?" + REST_AUTH_OPTIONS))
						.beanRef("MyBean", "getSessionId");

Hope this will help someone :-)

-Br, Roman




--
View this message in context: http://camel.465427.n5.nabble.com/Send-file-via-POST-request-tp5728674p5729109.html
Sent from the Camel - Users mailing list archive at Nabble.com.

Re: Send file via POST request

Posted by Sergey Beryozkin <sb...@gmail.com>.
On 08/03/13 11:56, jamalissimo wrote:
> Hi,
>
> yes, exactly. When I add QueryParam ,PathParam or both together, then no
> POST is send and file is not uploaded.
>
That is very strange. I've played with the CXF test where @Multipart 
annotations are used, added @PathParam there (actually returning 'null', 
same as in your case, given that no matching template variable exists in 
@Path("/uploadFile")), it works fine.

I think the best way forward is to get the cxfrs source and put a 
breakpoint there and see why adding these two extra parameters 
effectively breaks the route, can you please try that and let us know 
what happens ? You may've discovered some bug that needs to be fixed...

Thanks, Sergey

> -Roman
>
>
>
> --
> View this message in context: http://camel.465427.n5.nabble.com/Send-file-via-POST-request-tp5728674p5728821.html
> Sent from the Camel - Users mailing list archive at Nabble.com.


-- 
Sergey Beryozkin

Talend Community Coders
http://coders.talend.com/

Blog: http://sberyozkin.blogspot.com

Re: Send file via POST request

Posted by jamalissimo <ro...@gmail.com>.
Hi,

yes, exactly. When I add QueryParam ,PathParam or both together, then no
POST is send and file is not uploaded.

-Roman



--
View this message in context: http://camel.465427.n5.nabble.com/Send-file-via-POST-request-tp5728674p5728821.html
Sent from the Camel - Users mailing list archive at Nabble.com.

Re: Send file via POST request

Posted by Sergey Beryozkin <sb...@gmail.com>.
Hi,

On 08/03/13 11:07, jamalissimo wrote:
> Hi,
>
> I will describe what steps I need to do and what I have done. This route
> servers as file uploader but not in the right sense of the word. It does not
> upload file via stream but I need to send the file as part of post request
>
> What I need to do:
>
> 1. Get QueryParams from given url e.g.
> http://localhost:1555/cxf/127.0.0.1/uploadFile?port=443&user=myuser&password=mypassword&file=/path/to/myfile.txt
>
> 2. Set appropriate properties for passing data through the route
>
> 3. Pass this data to method where I set MultipartEntity and send the POST
> request
>
> --
>
> Yesterday I was able to make the consumer work as you saw at
> http://cxf.547215.n5.nabble.com/MultipartBody-with-PathParam-and-QueryParam-td5724291.html#a5724293
> <http://cxf.547215.n5.nabble.com/MultipartBody-with-PathParam-and-QueryParam-td5724291.html#a5724293>
>
> The tricky part is how to send different type of data then only multipart.
> For now it works with this hard-coded data:
>
> CXF:
> --------------------------------------------------------------------------------------
>
> 	@POST
> 	@Path("/upload")
> 	@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
> 	@Produces(MediaType.APPLICATION_JSON)
> 	public Response uploadFile(
> 		@Multipart(value = "session_id") String sessionId,
> 	        @Multipart(value = "action") String action
> 	){
> 		return null;
> 	}
>
> JAVA:
> --------------------------------------------------------------------------------------
>
> 	public void sendPost(Exchange exchange) throws IOException {
>
> 		String pathToUploader = "https://myserver.com/service/upload.php";
> 		
> 		String session_id = "0kk1sobmbuforgjamj10470a23";
> 		String action = "A";
> 		File myfile = new File("/path/to/myfile.txt");
> 		
> 		HttpClient client = new DefaultHttpClient();
> 		client.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION,
> HttpVersion.HTTP_1_1);
> 		HttpPost post = new HttpPost(pathToUploader);
> 		
> 		MultipartEntity entity = new MultipartEntity(
> HttpMultipartMode.BROWSER_COMPATIBLE );
> 		
> 			// For File parameters
> 			entity.addPart("myfile", new FileBody((( File ) myfile),
> "application/octet-stream" ));
> 			
> 			// For usual String parameters
> 			entity.addPart("session_id", new StringBody(session_id, "text/plain",
> Charset.forName( "UTF-8" )));
> 			entity.addPart("action", new StringBody(action, "text/plain",
> Charset.forName( "UTF-8" )));
> 			
> 			post.setEntity( entity );			
> 			
>         }
>
> This will send the correct http request via POST method and I can see file
> uploaded on my server.
>
> To add additional parameters I tried to do something like this:
>
> 	@POST
> 	@Path("/upload")
> 	@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
> 	@Produces(MediaType.APPLICATION_JSON)
> 	public Response uploadFile(
>                  @PathParam("host") String host,
> 		@QueryParam("port") String port,
> 		@Multipart(value = "session_id") String sessionId,
> 	        @Multipart(value = "action") String action
> 	){
> 		return null;
> 	}
>
> which actually works, I can get data via MessageContentsList, set properties
> and pass them to method sendPost().
>
> But this is the point where sending of multipart POST request stops working.
> I can see that everything is set correctly, but the request is sent with
> incorrect data or format and I can't see the uploaded file.

Are you saying that as soon as you add @PathParam("host") and 
@QueryParam("port") then the sendPost() stops working ?

Cheers, Sergey

>
> -Br, Roman
>
>
>
> --
> View this message in context: http://camel.465427.n5.nabble.com/Send-file-via-POST-request-tp5728674p5728817.html
> Sent from the Camel - Users mailing list archive at Nabble.com.



Re: Send file via POST request

Posted by jamalissimo <ro...@gmail.com>.
Hi,

I will describe what steps I need to do and what I have done. This route
servers as file uploader but not in the right sense of the word. It does not
upload file via stream but I need to send the file as part of post request

What I need to do:

1. Get QueryParams from given url e.g.
http://localhost:1555/cxf/127.0.0.1/uploadFile?port=443&user=myuser&password=mypassword&file=/path/to/myfile.txt

2. Set appropriate properties for passing data through the route

3. Pass this data to method where I set MultipartEntity and send the POST
request

--

Yesterday I was able to make the consumer work as you saw at 
http://cxf.547215.n5.nabble.com/MultipartBody-with-PathParam-and-QueryParam-td5724291.html#a5724293
<http://cxf.547215.n5.nabble.com/MultipartBody-with-PathParam-and-QueryParam-td5724291.html#a5724293>  

The tricky part is how to send different type of data then only multipart.
For now it works with this hard-coded data:

CXF:
--------------------------------------------------------------------------------------

	@POST
	@Path("/upload")
	@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
	@Produces(MediaType.APPLICATION_JSON)
	public Response uploadFile(
		@Multipart(value = "session_id") String sessionId,
	        @Multipart(value = "action") String action
	){
		return null;
	}

JAVA:
--------------------------------------------------------------------------------------

	public void sendPost(Exchange exchange) throws IOException {

		String pathToUploader = "https://myserver.com/service/upload.php";
		
		String session_id = "0kk1sobmbuforgjamj10470a23";
		String action = "A";
		File myfile = new File("/path/to/myfile.txt");
		
		HttpClient client = new DefaultHttpClient();
		client.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION,
HttpVersion.HTTP_1_1);
		HttpPost post = new HttpPost(pathToUploader);
		
		MultipartEntity entity = new MultipartEntity(
HttpMultipartMode.BROWSER_COMPATIBLE );
		
			// For File parameters
			entity.addPart("myfile", new FileBody((( File ) myfile),
"application/octet-stream" ));
			 
			// For usual String parameters
			entity.addPart("session_id", new StringBody(session_id, "text/plain",
Charset.forName( "UTF-8" )));
			entity.addPart("action", new StringBody(action, "text/plain",
Charset.forName( "UTF-8" )));
			
			post.setEntity( entity );			
			
       }

This will send the correct http request via POST method and I can see file
uploaded on my server. 

To add additional parameters I tried to do something like this:

	@POST
	@Path("/upload")
	@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
	@Produces(MediaType.APPLICATION_JSON)
	public Response uploadFile(
                @PathParam("host") String host,
		@QueryParam("port") String port,
		@Multipart(value = "session_id") String sessionId,
	        @Multipart(value = "action") String action
	){
		return null;
	}

which actually works, I can get data via MessageContentsList, set properties
and pass them to method sendPost(). 

But this is the point where sending of multipart POST request stops working.
I can see that everything is set correctly, but the request is sent with
incorrect data or format and I can't see the uploaded file.

-Br, Roman



--
View this message in context: http://camel.465427.n5.nabble.com/Send-file-via-POST-request-tp5728674p5728817.html
Sent from the Camel - Users mailing list archive at Nabble.com.

Re: Send file via POST request

Posted by Sergey Beryozkin <sb...@gmail.com>.
Hi,

Sorry for a delay; you've mentioned at CXF users that this all actually 
works except that you have to use some hard-coded values.

Can you explain what exactly is hard-coded, are you not able to get the 
parameters passed to cxfrs uploadFile method ? I think with cxfrs server 
one has to write a processor routine to get to the parameters, as in 
this form the cxfrs is only used to match the HTTP request.

May be, if all you have to do is to effectively proxy the request, the 
camel http component can be simpler to use; if you'd like to use cxfrs 
then consider using CXF JAXRS endpoint directly (use Camel Servlet and 
transport to link to it), have it processing GET and uploading to the 
destination with the code like this one:

http://cxf.apache.org/docs/jax-rs-multiparts.html#JAX-RSMultiparts-Uploadingfiles

and then return from the route.
However it sounds like it may be not the easiest approach, so I'm not 
100% sure

Cheers, Sergey



On 06/03/13 12:51, jamalissimo wrote:
> Hello guys,
>
> I am currently working on quite exciting assignment. Here is what I want to
> do:
>
> @GET REST response ->  CXF to get parameters from exchange ->  3rd Party API
> to get session_id ->  BEAN to send data via POST method and in
> multipart/mixed( it is for uploading files )
>
> My first problem is that I don't know how to set the REST, for now it looks
> like this:
>
> @GET
> @Path("/{myServerHost}/upload")
> @Produces(MediaType.APPLICATION_JSON)
> @Consumes(MediaType.MULTIPART_FORM_DATA)
>
> public Response uploadFile(@PathParam("myHost") String knowledgetreeHost,
> 			@QueryParam("myPort") String knowledgetreePort, @QueryParam("user")
> String user,
> 			@QueryParam("password") String password, @QueryParam("file") String file)
> {
> 		return null;
> }
>
> Then I have route and I am setting http method to post
>
> .when(header(CxfConstants.OPERATION_NAME).isEqualTo("uploadFile"))
>
> .recipientList("https://myserver.com/service/?method=login&username=myusername&password=123456")
>       .beanRef("myBean", "getSessionId")
>        // For now I needed data in GET(session_id),it is no problem, but in
> the following method
>        // I need to send POST request
>         .setHeader(Exchange.HTTP_METHOD, constant("POST"))
>          .beanRef("myBean","uploadMyFile")
> .endChoice()
>
> Method uploadMyFile should send POST request. For now it looks like this:
>
> 	public void uploadMyFile(Exchange exchange) throws IOException {
>                  //SESSION_ID is set in previous steps of route
> 		String sessionId = exchange.getProperty("SESSION_ID").toString();
>                 //FILENAME is taken from URL
>                 /**
>                    String filename = (String) msgList.get(4);
> 		  exchange.setProperty("FILENAME", filename);
>                  */
> 		String filename = "@"+exchange.getProperty("FILENAME").toString();
> 		String pathToUploader = "https://myserver.com/service/upload.php";
>
> 		// Instantiate an HttpClient
> 		HttpClient client = new HttpClient();
>
> 		// Instantiate a POST HTTP method
> 		PostMethod method = new PostMethod(pathToUploader);
> 		method.setRequestHeader("Content-type",
> exchange.getIn().getHeader("CONTENT_TYPE") + ";charset="+
> exchange.getIn().getHeader("CONTENT_ENCODING"));
>
> 		// Define name-value pairs to set into the QueryString
>
> 		NameValuePair nvp1 = new NameValuePair("session_id", sessionId);
> 		NameValuePair nvp2 = new NameValuePair("action", "A");
> 		NameValuePair nvp3 = new NameValuePair("myfile", filename);
> 		
> 		method.setQueryString(new NameValuePair[] { nvp1, nvp2, nvp3 });
>
> 		try {
> 			int statusCode = client.executeMethod(method);
>
> 			System.out.println("Status Code = " + statusCode);
> 			System.out.println("QueryString>>>  " + method.getQueryString());
> 			System.out.println("Status Text>>>" +
> HttpStatus.getStatusText(statusCode));
>
> 			// Get data as a String
> 			System.out.println(method.getResponseBodyAsString());
> 			
> 			exchange.getIn().setHeader("HTTP_METHOD", "POST");
> 			exchange.getIn().setBody(method.getResponseBodyAsString());
>
> 			// release connection
> 			method.releaseConnection();
> 		} catch (IOException e) {
> 			e.printStackTrace();
> 		}
>
> 	}
>
> Basically I need to know how to set REST and how to set POST method for CXF
>
> Thanks
>
> -Br, Roman
>
>
>
> --
> View this message in context: http://camel.465427.n5.nabble.com/Send-file-via-POST-request-tp5728674.html
> Sent from the Camel - Users mailing list archive at Nabble.com.