There is a good example showing how to put a file onto WebDAV server:
Java: How to upload a file to a WebDAV server from a servlet?
But how can I get a file content?
There is MethodPut class for the PUT command, but there is no appropriate GetMethod (although enum DavMethods.METHOD_GET is presented).
I solved the task 1) implementing my own class for the GET method 2) reading response bytes which represents file's content. I'd prefer to find a simpler solution in Jackrabbit still.
public class MyGetMethod extends DavMethodBase {
public MyGetMethod(String uri) {
super(uri);
}
public String getName() {
return DavMethods.METHOD_GET;
}
public boolean isSuccess(int statusCode) {
return statusCode == 200;
}
}
static void jackrabbitGet() throws Exception {
HttpClient client = new HttpClient();
Credentials creds = new UsernamePasswordCredentials("user", "pass");
client.getState().setCredentials(AuthScope.ANY, creds);
MyGetMethod method = new MyGetMethod(url goes here);
client.executeMethod(method);
if (method.isSuccess(method.getStatusCode())) {
byte[] resp = method.getResponseBody();
System.out.println("Got response: " + resp.length + " bytes");
}
}
Related
I want to send a request of a file through WebClient by POST method, and I need to send the file as byte[] to get right response.
I made MultipartFile file to byte[], and then I thought I need to use BodyInserters
to make this body contains byte[] but I don't know how to make that request body.
How to send a POST request that contains byte array by WebClient?
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
#RestController
public class ApiController {
#PostMapping(value = "/update")
public String update(#RequestParam("file") MultipartFile file, #RequestParam("uri") String uri ) {
String result = "{error : error}";
byte[] byteArr;
BodyInserters byteArrInserter;
try {
byteArr = file.getBytes();
A? publisher = B?.C?; // I don't know what will be right for those A?, B?, C?
byteArrInserter = BodyInserters.fromDataBuffers(publisher); // In fact, I'm not sure this method will be good for this situation, either.
} catch (Exception e) {
System.out.println(e);
}
WebClient client = WebClient.builder()
.codecs(configurer -> configurer.defaultCodecs().maxInMemorySize( 1024*1024*1024 * 2)) // 2GB
.build();
try {
result = client
.post()
.uri(uri)
.body(byteArrInserter)
.retrieve().bodyToMono(String.class).block();
} catch (Exception e) {
System.out.println(e);
}
return result;
}
}
I don't know how to send binary content with WebClient, but there are some other options. First there is SpringBoot own RestTemplate class which is an alternative to WebClient. Here is an comparative article about them both: Spring WebClient vs. RestTemplate. If you wish to use third party Http clients that definitely can send binary content you can look at 2 popular choices:
Apache Http Client
OK Http Client
And finally, I wrote my own Http client that is part of Open-source MgntUtils library written and maintained by me. This Http client is very simplistic but does the job. I am actually at the process of adding the feature that allows to upload binary info. I haven't published it yet, but you can download the branch or look at the code how I do it there. The branch is located here The HttpClient class is here. See method public String sendHttpRequest(String requestUrl, HttpMethod callMethod, ByteBuffer data) throws IOException at line 162. The method with which I tested this method looks like this:
private static void testHttpClientBinaryUpload() {
try {
byte[] content = Files.readAllBytes(Paths.get("C:\\Michael\\Personal\\pics\\testPic.jpg"));
HttpClient client = new HttpClient();
Integer length = content.length;
Files.write(Paths.get("C:\\Michael\\tmp\\testPicOrig.jpg"), content);
client.setRequestHeader("Content-Length", length.toString());
String result = client.sendHttpRequest("http://localhost:8080/upload", HttpMethod.POST, ByteBuffer.wrap(content));
System.out.println(result);
System.out.println("HTTP " + client.getLastResponseCode() + " " + client.getLastResponseMessage());
} catch (Exception e) {
System.out.println(TextUtils.getStacktrace(e, "com.mgnt."));
}
}
And server side Springboot rest controller that receives the request looks like this:
#RestController
#RequestMapping("/upload")
public class UploadTestController {
#PostMapping
public ResponseEntity<String> uploadTest(HttpServletRequest request) {
try {
String lengthStr = request.getHeader("content-length");
int length = TextUtils.parseStringToInt(lengthStr, -1);
if(length > 0) {
byte[] buff = new byte[length];
ServletInputStream sis =request.getInputStream();
int counter = 0;
while(counter < length) {
int chunkLength = sis.available();
byte[] chunk = new byte[chunkLength];
sis.read(chunk);
for(int i = counter, j= 0; i < counter + chunkLength; i++, j++) {
buff[i] = chunk[j];
}
counter += chunkLength;
if(counter < length) {
TimeUtils.sleepFor(5, TimeUnit.MILLISECONDS);
}
}
Files.write(Paths.get("C:\\Michael\\tmp\\testPic.jpg"), buff);
}
} catch (Exception e) {
System.out.println(TextUtils.getStacktrace(e));
}
return ResponseEntity.ok("Success");
}
}
I am planning to publish the new version of the library in few days and this feature will be included. In any case here is the link to Maven Artifacts, Github and Javadoc
I recently encountered some problems with java.net.http.HttpClient that comes with JDK 11. I don't know how to use file upload. Found the ofInputStream() in java.net.http.BodyPublishers. I don't know if I using this method file upload.
Here are the examples I wrote.
public HttpResponse<String> post(String url, Supplier<? extends InputStream> streamSupplier, String... headers) throws IOException, InterruptedException {
HttpRequest.Builder builder = HttpRequest.newBuilder()
.uri(URI.create(url))
.headers(headers)
.POST(null == streamSupplier ?
HttpRequest.BodyPublishers.noBody() : HttpRequest.BodyPublishers.ofInputStream(streamSupplier));
HttpRequest request = builder.build();
log.debug("Execute HttpClient Method:『{}』, Url:『{}』", request.method(), request.uri().toString());
return client.send(request, HttpResponse.BodyHandlers.ofString());
}
The HttpRequest type provide factory method for creating request publisher for handling body type such as file:
HttpRequest.BodyPublishers::ofFile(Path)
You can update your method:
public HttpResponse<String> post(String url, Path file, String... headers) throws IOException, InterruptedException {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.headers(headers)
.POST(null == file ? HttpRequest.BodyPublishers.noBody() :
HttpRequest.BodyPublishers.ofFile(file))
.build();
log.debug("Execute HttpClient Method:『{}』, Url:『{}』", request.method(),
request.uri().toString());
return client.send(request, HttpResponse.BodyHandlers.ofString());
}
The java.net.http.HttpClient handles bytes supplied through the BodyPublisher as raw body data, without any interpretation. Whether you use HttpRequest.BodyPublishers::ofFile(Path) or HttpRequest.BodyPublishers::ofByteArray(byte[]) is therefore semantically irrelevant: what changes is simply how the bytes that will be transmitted are obtained.
In case of file upload - your server probably expects that the request body will be formatted in certain ways. It might also expect some specific headers to be transmitted with the request (such as Content-Type etc). The HttpClient will not do that magically for you. This is something you need to implement at the caller level.
you may use the method by:
public void uploadLocalFileToRemote(String notEncodedUrlStr, String remoteFilename, String localSourceDir, String localFilename) {
Path sourcePath = Path.of(localSourceDir, localFilename);
if(!sourcePath.toFile().canRead())
{
System.err.println("please check the local file existance/readability: " + sourcePath.toAbsolutePath());
return;
}
FileInputStream ins = null;
try {
ins = new FileInputStream(sourcePath.toFile());//FileNotFoundException extends IOException
BufferedInputStream buf_ins = new BufferedInputStream(ins);
Supplier<? extends InputStream> streamSupplier = new Supplier<BufferedInputStream>() {
#Override
public BufferedInputStream get() {
return buf_ins;
}
};
//HttpResponse<String> response = post(notEncodedUrlStr, streamSupplier,
HttpResponse<String> response = post(notEncodedUrlStr, () -> buf_ins,
"User-Agent", "Java 11 HttpClient Bot", "Content-type", "application/octet-stream",
"accept", "*/*", "fileName", remoteFilename);
// print response:
System.out.println(response.version().name() + " " + response.statusCode());
// print response headers
HttpHeaders headers = response.headers();
headers.map().forEach((k, v) -> System.out.println(k + ":" + v));
// print response body
String body = response.body();
System.out.println(body);
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
another consideration is how your server side is implemented. here assume the server side will using http 1.1 "chunked". and configured a directory for remoteFilename.
I have made my rest web service code to start sever like this :
static final String BASE_URI = "http://10.236.51.14:9000/abcd/";
public static void main(String[] args) {
try {
HttpServer server = HttpServerFactory.create(BASE_URI);
server.start();
System.out.println("Press Enter to stop the server. ");
System.in.read();
server.stop(0);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
And in the rest web service I have made a basic code to receive 2 arguments and show their sum like this :
#GET
#Path("/add/{a}/{b}")
#Produces(MediaType.TEXT_XML)
public String add(#PathParam("a") double a, #PathParam("b") double b) {
return "<?xml version=\"1.0\"?>" + "<result>" + (a + b) + "</result>";
}
I want to send Json data (image) from my android app to this webservice but I don't know how to receive it in webservice and display it.
Here is the code from my android app. In this I have converted a bitmap to string using Base64. How should I send it to my webservice?
ByteArrayOutputStream baos = new ByteArrayOutputStream();
mybitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
byte[] b = baos.toByteArray();
String strBitMap = Base64.encodeToString(b, Base64.DEFAULT);
Any help will be appreciated :)
I have searched a lot but cant find appropriate code for my webservice to receive and display the json data. I am also struggling in sending this base64 string to the webservice in form of json.
Please help me out.
Best regards :)
I have a question: Does your example WebService work? I mean the one with the two arguments. If you call http://10.236.51.14:9000/abcd/add/1/2 in your browser does it display 3 correctly? If not you should have an ApplicationConfig containing your REST-interfaces. Those should be added as resource classes for example like this:
#ApplicationPath("api")
public class ApplicationConfig extends Application {
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> resources = new HashSet<>();
resources.addAll(addServiceClasses());
resources.addAll(addFilterClasses());
return resources;
}
private Set<Class<?>> addServiceClasses() {
// add all your REST-classes here
Set<Class<?>> resources = new HashSet<>();
resources.add(YourCalculatorRestServiceClass.class);
resources.add(YourImageConsumingRestServiceClass.class);
return resources;
}
private Set<Class<?>> addFilterClasses() {
// add all your filter classes here (if you have any)
Set<Class<?>> resources = new HashSet<>();
resources.add(YourAuthenticationFilterClass.class);
resources.add(OtherFilterClass.class);
return resources;
}
#Override
public Map<String, Object> getProperties() {
Map<String, Object> properties = new HashMap<>();
// in Jersey WADL generation is enabled by default, but we don't
// want to expose too much information about our apis.
// therefore we want to disable wadl (http://localhost:8080/service/application.wadl should return http 404)
// see https://jersey.java.net/nonav/documentation/latest/user-guide.html#d0e9020 for details
properties.put("jersey.config.server.wadl.disableWadl", true);
// we could also use something like this instead of adding each of our resources
// explicitly in getClasses():
// properties.put("jersey.config.server.provider.packages", "com.nabisoft.tutorials.mavenstruts.service");
return properties;
}
}
That should make the deal and you should be able to call http://10.236.51.14:9000/abcd/api/add/1/2. ApplicationConfig is annotated with #Path("api"). That means all classes registered in this config have the root path http://your.server.address/api/.
Now to your problem. I assume your server is working and you can reach your Webservice /add/1/2 displaying the result 3 in your browser.
Now you need another service listening for a POST. I'd take your already prepared String as the posted body.
#Path("image")
public class ImageReceiverRestService {
#POST
public Response checkAssignable(String base64ImageString) {
// code here working with the base64ImageString
// response code according to whatever happened during your algorithm
return Response.ok().build();
}
}
For appropriate HTTP response codes see this Wikipedia article for a quick overview HTTP Status Codes
So now you'd need a corresponding client on your android app. For example:
public class ImageSendingRestClient {
private final static String SERVER_BASE_URI = "http://10.236.51.14:9000/abcd/api/";
private final static String API_ADDRESS = "image/";
public ImageSendingRestClient() {
}
#Override
public void sendImageStringForProcessing(String base64ImageString) throws Exception {
Entity<String> entity = Entity.json(base64ImageString);
Response response = ClientBuilder.newClient()
.target(SERVER_BASE_URI)
.path(API_ADDRESS)
.request()
.post(entity);
try {
if (response.getStatus() == Response.Status.OK.getStatusCode()) {
return;
}
if (response.getStatus() == Response.Status.NOT_FOUND.getStatusCode()) {
throw new Exception;
}
} finally {
response.close();
}
}
}
All dependencies needed are JAX-RS implementations like JAX-RS reference implementation Jersey. Maybe you should also check the Jersey Guide with many examples providing most of the information you need Jersey User Guide
I'm using Codename One to upload videos to Vimeo using their respective API's. I'm using a multipart request to actually upload the file, but a response is required to find the upload status. How can I get the response?
public void doFileUpload(String url, String filename) throws IOException {
MultipartRequest req = new MultipartRequest() {
int chr;
StringBuffer sb = new StringBuffer();
public String response = "";
public void readResponse(InputStream input) throws IOException {
response = Util.readToString(input);
Log.p("File Response->" + response);
}
protected void handleErrorResponseCode(int code, String message)
{
Log.p("Error Response->" + message);
}
protected void readHeaders(Object connection) throws IOException {
String val = getHeader(connection, "MyHeaderName");
Log.p("Header Response->" + val);
}
protected void handleException(Exception err) {
Dialog.show(
"Connection Err!!",
"Are you connected to the internet? Check your connection",
"Ok", null);
}
};
req.setUrl(url);
req.setPost(true);
String mime = "application/mp4";
req.addData("file_data", filename, mime);
req.setFilename("file_data", filename);
req.setReadResponseForErrors(true);
req.addResponseCodeListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
try {
NetworkEvent event = (NetworkEvent) ev;
Log.p("Err Rsp Code->" + event.getResponseCode());
Log.p("ResponseCodeListener:");
Log.p(ev.toString() );
I suggest you use the addData method that accepts a file URL rather than a byte array since you might exceed device memory with the byte array version (e.g. for a larger video or low memory device).
You can read the response just like any other connection request:
Derive the connection request and override: protected void readResponse(InputStream input)
Use addResponseListener to bind a listener to the users response.
Use addToQueueAndWait to detect that the upload finished then just invoke getResponseData() to get the byte array data.
I had the same problem! on the back-end, I am using Spring boot, my upload controller was simply tagged as #Controller when changed to #RestController, it worked fine and I got readResponse(InputStream input) callback, where I handle the response as JSON.
I'm currently using Jersey as a proxy REST api to call another RESTful web service. Some of the calls will be passed to and from with minimal processing in my server.
Is there a way to do this cleanly? I was thinking of using the Jersey Client to make the REST call, then converting the ClientResponse into a Response. Is this possible or is there a better way to do this?
Some example code:
#GET
#Path("/groups/{ownerID}")
#Produces("application/xml")
public String getDomainGroups(#PathParam("ownerID") String ownerID) {
WebResource r = client.resource(URL_BASE + "/" + URL_GET_GROUPS + "/" + ownerID);
String resp = r.get(String.class);
return resp;
}
This works if the response is always a success, but if there's a 404 on the other server, I'd have to check the response code. In other words, is there clean way to just return the response I got?
There is no convenience method as far as I am aware. You can do this:
public Response getDomainGroups(#PathParam("ownerID") String ownerID) {
WebResource r = client.resource(URL_BASE + "/" + URL_GET_GROUPS + "/" + ownerID);
ClientResponse resp = r.get(ClientResponse.class);
return clientResponseToResponse(resp);
}
public static Response clientResponseToResponse(ClientResponse r) {
// copy the status code
ResponseBuilder rb = Response.status(r.getStatus());
// copy all the headers
for (Entry<String, List<String>> entry : r.getHeaders().entrySet()) {
for (String value : entry.getValue()) {
rb.header(entry.getKey(), value);
}
}
// copy the entity
rb.entity(r.getEntityInputStream());
// return the response
return rb.build();
}
for me answer from Martin throw:
JsonMappingException: No serializer found for class sun.net.www.protocol.http.HttpURLConnection$HttpInputStream
Change from
rb.entity(r.getEntityInputStream());
to
rb.entity(r.getEntity(new GenericType<String>(){}));
helped.