2

I need to upload zipped shapefiles to GeoServer through REST. The GeoServer provides an example CURL command to upload the shapefile zip as follows:

curl -v -u admin:geoserver -XPUT -H "Content-type: application/zip" --data-binary @data.zip http://172.16.17.86:9090/geoserver/rest/workspaces/IDIRA6/datastores/Murrindindi/file.shp

The above CURL command works fine. However, I want to do this upload in Java httpclient. I am a newbie to Java but I have written the following code to achieve this:

import java.io.FileInputStream;
import java.io.InputStream;

import org.apache.http.HttpEntity;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

public class QuickStart {

    public static void main(String[] args) throws Exception {
     String file="data.zip";       
        
        CredentialsProvider credsProvider = new BasicCredentialsProvider();
        credsProvider.setCredentials(
                AuthScope.ANY,
                new UsernamePasswordCredentials("admin", "geoserver"));
        
        CloseableHttpClient httpclient = HttpClients.custom()
                .setDefaultCredentialsProvider(credsProvider)
                .build();
        
        try {
            HttpPut httpPut = new HttpPut("http://172.16.17.86:9090/geoserver/rest/workspaces/IDIRA6/datastores/Murrindindi/file.shp");
            File file2 = new File("data.zip");                
            InputStream inputStream = new FileInputStream(file);          
            

            HttpEntity reqEntity = MultipartEntityBuilder.create()    
                    .addPart("file", new FileBody(file2, ContentType.DEFAULT_BINARY))                  
                    .addBinaryBody("upstream", inputStream, ContentType.create("application/zip"), "data.zip")
                    .build();

            httpPut.setEntity(reqEntity);

            System.out.println("executing request " + httpPost.getRequestLine());
            CloseableHttpResponse response = httpclient.execute(httpPut );
            try {
                System.out.println("----------------------------------------");
                System.out.println(response.getStatusLine());
                HttpEntity resEntity = response.getEntity();
                if (resEntity != null) {
                    System.out.println("Response content length: " + resEntity.getContentLength());
                }
                EntityUtils.consume(resEntity);
            } finally {
                response.close();
            }
        } finally {
            httpclient.close();
        }
    }

}

When I run this code I get the following error:

INFO: I/O exception (java.net.SocketException) caught when processing request to {}->http://172.16.17.86:9090: 
Connection reset by peer: socket write error
Exception in thread "main" org.apache.http.client.ClientProtocolException

Any idea how this can be resolved? I have tested a POST with simple authentication and that works. However, there is an issue when I try to upload the zip file.

The curl output is as follows:

curl -v -u admin:geoserver -XPUT -H "Content-type: application/zip" --data-binary @Murrindindi_OvalCorrected.zip http://172.16.17.86:9090/geoserver/rest/workspaces/IDIRA6/datastores/Murrindindi/file.shp

* Trying 172.16.17.86...
* TCP_NODELAY set
* Connected to 172.16.17.86 (172.16.17.86) port 9090 (#0)
* Server auth using Basic with user 'shapeuploader'
> PUT /geoserver/rest/workspaces/IDIRA6/datastores/Murrindindi/file.shp HTTP/1.1
> Host: 172.16.17.86:9090
> Authorization: Basic c2hhcGV1cGxvYWRlcjo2OHJpOURwazdJR001bEo=
> User-Agent: curl/7.63.0
> Accept: */*
> Content-type: application/zip
> Content-Length: 7022252
> Expect: 100-continue
>
< HTTP/1.1 100
* We are completely uploaded and fine
< HTTP/1.1 201
< X-Frame-Options: SAMEORIGIN
< Content-Length: 0
< Date: Sat, 23 Mar 2019 23:04:27 GMT
* Connection #0 to host 172.16.17.86 left intact
Pino
  • 7,468
  • 6
  • 50
  • 69
  • Just a tip, but the curl command uses the HTTP PUT method and your Java code uses HTTP POST. The two thing is different, and you should always use the correct one. – Balázs Nemes Mar 22 '19 at 12:30
  • Thanks, I modified the Java code to use HttpPut but I still get the same error. HttpPut httpPost = new HttpPut("http://172.16.17.86:9090/geoserver/rest/workspaces/IDIRA6/datastores/Murrindindi/file.shp"); – Anand Veeraswamy Mar 22 '19 at 12:38
  • 2nd tip: I think you should use that `MultipartEntity` class to send the file. See it here https://stackoverflow.com/questions/6917105/java-http-client-to-upload-file-over-post This example uses post, but you still have to use put. – Balázs Nemes Mar 22 '19 at 12:46
  • Thanks again, I have does this by adding two lines of code:File file2 = new File("data.zip"); .addPart("file", new FileBody(file2)) Hope this is correct. But I still get the same error. – Anand Veeraswamy Mar 22 '19 at 13:01

1 Answers1

0

After having compared raw HTTP data generated by curl with the one generated by your code I have found these differences:

  • curl doesn't generate a multipart request
  • curl generates an Expect: 100-continue header
  • curl uses preemptive basic auth

The following code should be equivalent to that curl command however I cannot test it with GeoServer. I have used the fluent API of apache-httpclient (that is fluent-hc-4.5.7.jar). Let me know if it works.

import java.io.File;
import org.apache.http.client.fluent.*;
import org.apache.http.entity.ContentType;

public class QuickStart {

    public static void main(String[] args) throws Exception {
        File file = new File("data.zip");
        Executor executor = Executor.newInstance()
                .auth("admin", "geoserver")
                .authPreemptive("172.16.17.86:9090");
        String response = executor.execute(Request.Put("http://172.16.17.86:9090/geoserver/rest/workspaces/IDIRA6/datastores/Murrindindi/file.shp")
                .useExpectContinue()
                .bodyFile(file, ContentType.create("application/zip")))
                .returnResponse()
                .toString();
        System.out.println(response);
    }

}
Pino
  • 7,468
  • 6
  • 50
  • 69
  • This works perfectly and is simple code, thanks. Only issue the println statement does not print anything. It will be useful to get the status of the PUT operation. – Anand Veeraswamy Mar 25 '19 at 15:00
  • Your curl output says "Content-Length: 0", it means that the server doesn't respond anything. I've replaced the output of the response body with the output of the HTTP result code. – Pino Mar 25 '19 at 15:19