Sending base64 image via http request in Java miss up some chars - java

I've been trying to send a base64 image using java to NodeJS API, after working and searching for hours I could not know what might cause the following problem, the problem as following:
After logging the base64 image in nodejs I see all + chars replaced by space
Here's a part of the original base64 in Java
f8A0NH2qH+/+hooouAfaof7/wCho+1Q/
and here's is a part of the received image in NodeJS
f8A0NH2qH / hooouAfaof7/wCho 1Q/
I've tried to send an image via POSTMAN and no problem at all.
All steps as following:
1- I am converting an image to base64 using the following snippet
public static String imgToBase64String(final RenderedImage img, final String formatName) {
final ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
ImageIO.write(img, formatName, Base64.getEncoder().wrap(os));
return os.toString(StandardCharsets.ISO_8859_1.name());
} catch (final IOException ioe) {
throw new UncheckedIOException(ioe);
}
}
public static BufferedImage base64StringToImg(final String base64String) {
try {
return ImageIO.read(new ByteArrayInputStream(Base64.getDecoder().decode(base64String)));
} catch (final IOException ioe) {
throw new UncheckedIOException(ioe);
}
}
And to take screenshot
final Robot robot = new Robot();
final Rectangle r = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
final BufferedImage bi = robot.createScreenCapture(r);
final String base64String = Base64Converter.imgToBase64String(bi, "jpg");
2- I am using Gson library to stringify object
3- I am using bodyParser in NodeJS
4- Sending HTTP request as:
public static void sendPOST(String image) throws Exception {
String POST_PARAMS = "screenShotData";
URL obj = new URL(POST_URL);
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
con.setRequestMethod("POST");
con.setConnectTimeout(5000); // 5 seconds
con.setReadTimeout(5000); // 5 seconds
Gson gson = new Gson();
Http.ScreenShot screenShot = new ScreenShot(); // This is just a class with a string property
screenShot.setImage(image);
POST_PARAMS += gson.toJsonTree(screenShot).getAsJsonObject();
con.setDoOutput(true);
OutputStream os = con.getOutputStream();
byte[] outputBytesArray = POST_PARAMS.getBytes();
os.write(outputBytesArray);
os.flush();
os.close();
int responseCode = con.getResponseCode();
System.out.println("POST Response Code :: " + responseCode);
if (responseCode == HttpURLConnection.HTTP_OK) { //success
BufferedReader in = new BufferedReader(new InputStreamReader(
con.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
Object responseObject = gson.fromJson(response.toString(), Object.class);
System.out.println("Res: " + responseObject);
} else {
System.out.println(con.getResponseMessage());
}
}

In URL encoded text, the + character means a space character. For example,
https://example.com/?s=nodejs+bodyparser
sends the s parameter with the value
nodejs bodyparser
(notice the space).
When you do an ordinary form post (the kind browsers do) you use the application/x-www-form-urlencoded data type, meaning the payload of your POST operation looks like a query string. I think you are passing a JSON object as a text string without url-encoding it.
You probably want to use the application/json data type instead. nodejs's body parser detects, from your Content-type header, that it's JSON and parses it correctly.
Try this. (not debugged, sorry.)
string payload = gson.toJsonTree(screenShot).getAsJsonObject();
byte[] outputBytesArray = payload.getBytes();
con.setRequestProperty("Content-Type", "application/json");
con.setDoOutput(true);
OutputStream os = con.getOutputStream();
os.write(outputBytesArray);
os.flush();
os.close();

You forgot to close the wrapped Base64 encoder stream. Only if you close it the end of the base64 encoded data can be written:
public static String imgToBase64String(final RenderedImage img, final String formatName) {
final ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
try (OutputStream wrapped = Base64.getEncoder().wrap(os)) {
ImageIO.write(img, formatName, wrapped);
}
return os.toString(StandardCharsets.ISO_8859_1.name());
} catch (final IOException ioe) {
throw new UncheckedIOException(ioe);
}
}

I had faced this issue, but I had no authority to change in class or something else.
I had just simply replaced space with +, in most of the cases it was working.

Related

How to print/read image from REST API call using JAVA

I have one REST URL which if I use in postman it gives me image in response. Now, I'm trying to see the same thing with JAVA code in Eclipse. (I'm looking for either saving image file or showing with JFrame or Applet etc.)
String url = "https://test-api.com/v1/abc.jpg";
// Creating URL Connection
URL obj = new URL(url);
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
// Defining the request method. It can be GET, POST, PUT, DELETE
con.setRequestMethod("GET");
// Getting the HTTP Response code like 200, 404 etc.
int responseCode = con.getResponseCode();
System.out.println("\nSending 'GET' request to URL : " + url + "\n");
System.out.println("Response Code : " + responseCode + "\n");
// Saving response in the Stringbuffer
BufferedReader in = new BufferedReader(
new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
byte bytes[] = String.valueOf(response).getBytes();
InputStream inS = new ByteArrayInputStream(bytes);
int width = 963; //width of the image
int height = 640; //height of the image
BufferedImage image = null;
image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
BufferedImage bImageFromConvert = ImageIO.read(inS);
When I run the code, I get the exception like below.
javax.imageio.IIOException: Invalid JPEG file structure: two SOI markers
at com.sun.imageio.plugins.jpeg.JPEGImageReader.readImageHeader(Native Method)
at com.sun.imageio.plugins.jpeg.JPEGImageReader.readNativeHeader(JPEGImageReader.java:628)
at com.sun.imageio.plugins.jpeg.JPEGImageReader.checkTablesOnly(JPEGImageReader.java:347)
at com.sun.imageio.plugins.jpeg.JPEGImageReader.gotoImage(JPEGImageReader.java:495)
at com.sun.imageio.plugins.jpeg.JPEGImageReader.readHeader(JPEGImageReader.java:621)
at com.sun.imageio.plugins.jpeg.JPEGImageReader.readInternal(JPEGImageReader.java:1078)
at com.sun.imageio.plugins.jpeg.JPEGImageReader.read(JPEGImageReader.java:1058)
at javax.imageio.ImageIO.read(ImageIO.java:1448)
at javax.imageio.ImageIO.read(ImageIO.java:1352)
Response code comes 200. So, not sure what I'm doing wrong here. Why ImageIO cannot read the inputstream and picking up two SOI marker.
To show an image on the browser using jax-rs you can use this method with the hypothese that you have images folder under resources in your classpath :
#GET
#Path("image")
#Produces({"image/jpeg"})
public Response getImage() {
BufferedImage image = null;
byte[] imageData = null;
try {
//System.out.println("chemin courant : " + ClassLoader.getSystemClassLoader().
// getResource(".").getPath());
image = ImageIO.read(ClassLoaderTestResource.class
.getResourceAsStream("/images/yourimage.jpg"));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(image, "png", baos);
imageData = baos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("image : "+ image);
System.out.println("Image data : "+ imageData);
return Response.ok(new ByteArrayInputStream(imageData)).build();
}
so if you call http://localhost:8080/image for example you will have your image in the browser.

Reading rest service of content type application/vnd.oracle.adf.resourceitem+json

I have a webservice whose content type is application/vnd.oracle.adf.resourceitem+json.
The HttpEntity of the reponse obtained by hitting this service is looks like this
ResponseEntityProxy{[Content-Type: application/vnd.oracle.adf.resourceitem+json,Content-Length: 3,Chunked: false]}
When I try to convert this HttpEntity into String it gives me a blank String {}.
Below are the ways I tried to convert the HttpEntity to String
1.
String strResponse = EntityUtils.toString(response.getEntity());
2.
String strResponse = "";
String inputLine;
BufferedReader br = new BufferedReader(new InputStreamReader(entity.getContent()));
try {
while ((inputLine = br.readLine()) != null) {
System.out.println(inputLine);
strResponse += inputLine;
}
br.close();
} catch (IOException e) {
e.printStackTrace();
}
3.
response.getEntity().writeTo(new FileOutputStream(new File("C:\\Users\\harshita.sethi\\Documents\\Chabot\\post.txt")));
All returns String -> {}.
Can anyone tell me what am I doing wrong?
Is this because of the content type?
The above code is still giving the same response with empty JSON object. So I modified and wrote the below code. This one seems to run perfectly fine.
URL url = new URL(urlString);
HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
con.setDoOutput(true);
con.setRequestMethod("POST");
con.addRequestProperty("Authorization", getAuthToken());
con.addRequestProperty("Content-Type", "application/vnd.oracle.adf.resourceitem+json;charset=utf-8");
String input = String.format("{\"%s\":\"%s\",\"%s\":\"%s\"}", field, value, field2, value2);
System.out.println(input);
OutputStream outputStream = con.getOutputStream();
outputStream.write(input.getBytes());
outputStream.flush();
con.connect();
System.out.println(con.getResponseCode());
// Uncompressing gzip content encoding
GZIPInputStream gzip = new GZIPInputStream(con.getInputStream());
StringBuffer szBuffer = new StringBuffer();
byte tByte[] = new byte[1024];
while (true) {
int iLength = gzip.read(tByte, 0, 1024);
if (iLength < 0) {
break;
}
szBuffer.append(new String(tByte, 0, iLength));
}
con.disconnect();
returnString = szBuffer.toString();
Authentication method
private String getAuthToken() {
String name = user;
String pwd = this.password;
String authString = name + ":" + pwd;
byte[] authEncBytes = Base64.getEncoder().encode(authString.getBytes());
System.out.println(new String(authEncBytes));
return "Basic " + new String(authEncBytes);
}
In case anybody faces the same issue. Let me share the challenged I faced and how I rectified those.
The above code works for all content-types/methods. Can be used for any type (GET, POST, PUT,DELETE).
For my requirement I had a POST webservice with
Content-Encoding →gzip
Content-Type →application/vnd.oracle.adf.resourceitem+json
Challenges : I was able to get the correct response code but I was getting junk characters as my response string.
Solution : This was because the output was compressed in gzip format which needed to be uncompressed.
The code of uncompressing the gzip content encoding is also mentioned above.
Hope it helps future users.

Sending image from java client to python server

I'm new to java and I've been stuck on this problem for the last few days. I'm sending an image from a java client to a python server. I'm getting a 200 request, meaning the connection is being made. But the image is not being read on the server side. I've included the code - any help would be great.
Server Code
def index(request):
data = {"POST REQUEST, INSIDE INDEX, NO URL OR IMAGE SENT": False}
if request.method == "GET":
return HttpResponse("GET REQUEST WORKS. RR.")
if request.method == "POST":
# check to see if an image was uploaded
if request.FILES.get("image", None) is not None:
# grab the uploaded image
image = request.FILES["image"]
# otherwise, assume that a URL was passed in
else:
# grab the URL from the request
url = request.POST.get("url", None)
# if the URL is None, then return an error
if url is None:
return HttpResponse("no URL is specified")
# load the image and convert
image = _grab_image(url=url)
req = image
arr = np.asarray(bytearray(req.read()), dtype=np.uint8)
img = cv2.imdecode(arr,-1) # 'load it as it is'
Server = True
Student_Answers = []
image = doc_Scan(img)
if (image == "ERROR"):
return HttpResponse("TAKE ANOTHER PICTURE")
else:
images = crop_answer2(image)
for i in range(0,8):
ans = images[i]
Answer = Input_Image(ans,Server)
print ('Final Answer = ', Answer)
Student_Answers.append(Answer)
Results, score = mark_paper(Server,Student_Answers)
Worded_Respose = "The Student got ", score, " correct"
return HttpResponse(Results, content_type="application/json")
else:
return HttpResponse("NO POST REQUEST")
Client Code
public static void main(String [] args) throws Exception{
String url = "http://########";
// 2. create obj for the URL class
URL obj = new URL(url);
// 3. open connection on the url
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
con.setRequestMethod("POST");
con.setDoOutput(true);
try {
System.out.println("Reading image from disk. ");
img = ImageIO.read(new File("image.jpg"));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(img, "jpg", baos);
baos.flush();
byte[] bytes = baos.toByteArray();
baos.close();
System.out.println("Sending image to server. ");
OutputStream out = con.getOutputStream();
DataOutputStream image = new DataOutputStream(out);
image.writeInt(bytes.length);
image.write(bytes, 0, bytes.length);
System.out.println("Image sent to server. ");
image.close();
// close the output stream
out.close();
}catch (Exception e) {
System.out.println("Exception: " + e.getMessage());
soc.close();
}
// define object for the reply from the server
BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
//Get response from server
int responseCode = con.getResponseCode();
System.out.println("Response Code : " + responseCode);
// read in the response from the server
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
System.out.println(inputLine);
}
// close the input stream
in.close();
}
}

How to send string compressed with GZIP from Java App to PHP web service

I have this issue with GZIP compression:
I need to send by POST method a huge JSON string, which is too big to be accept like URL (Ex: http://localhost/app/send/JSON STRING ENCODED BY BASE64), than it result in HTTP error 403
so, I need to compress my json and I found a way to do it with GZIP compression, which I can decompress with gzdecode() in PHP.
but it doesn't work...
my functions compress() and decompress() works fine inside my Java App, but when I send it to webservice, something goes wrong and gzdecode() doesn't work.
I have no idea what I missing, I need some help
functions used in java app (client)
public String Post(){
String retorno = "";
String u = compress(getInput());
u = URLEncoder.encode(URLEncoder.encode(u, "UTF-8"));
URL uri = new URL(url + u);
HttpURLConnection conn = (HttpURLConnection) uri.openConnection();
conn.setDoOutput(false);
conn.setRequestMethod(getMethod());
conn.setRequestProperty("Content-encoding", "gzip");
conn.setRequestProperty("Content-type", "application/octet-stream");
BufferedReader buffer = new BufferedReader(
new InputStreamReader((conn.getInputStream())));
String r = "";
while ((r = buffer.readLine()) != null) {
retorno = r + "\n";
}
return retorno;
}
GZIP compress function (client)
public static String compress(String str) throws IOException {
byte[] blockcopy = ByteBuffer
.allocate(4)
.order(java.nio.ByteOrder.LITTLE_ENDIAN)
.putInt(str.length())
.array();
ByteArrayOutputStream os = new ByteArrayOutputStream(str.length());
GZIPOutputStream gos = new GZIPOutputStream(os);
gos.write(str.getBytes());
gos.close();
os.close();
byte[] compressed = new byte[4 + os.toByteArray().length];
System.arraycopy(blockcopy, 0, compressed, 0, 4);
System.arraycopy(os.toByteArray(), 0, compressed, 4,
os.toByteArray().length);
return Base64.encode(compressed);
}
method php used to receive a URL (server, using Slim/PHP Framework)
init::$app->post('/enviar/:obj/', function( $obj ) {
$dec = base64_decode(urldecode( $obj ));//decode url and decode base64 tostring
$dec = gzdecode($dec);//here is my problem, gzdecode() doesn't work
}
post method
public Sender() throws JSONException {
//
url = "http://192.168.0.25/api/index.php/enviar/";
method = "POST";
output = true;
//
}
As noticed in some of the comments.
Bigger data should be send as a POST request instead of GET. URL params should be used only for single variables. As you noticed the URL length is limited to few kB and it's not very good idea to send larger data this way (even though GZIP compressed).
Your GZIP compression code seems to be wrong. Please try this:
public static String compress(String str) throws IOException {
ByteArrayOutputStream os = new ByteArrayOutputStream(str.length());
GZIPOutputStream gos = new GZIPOutputStream(os);
gos.write(str.getBytes());
os.close();
gos.close();
return Base64.encodeToString(os.toByteArray(),Base64.DEFAULT);
}

how to send byte array in json post request?

I have a wcf service which accepts byte[] serialData, now am developing a java client which needs to consume the same method.
When i sent bytearray to the service as a json post request , it is getting an exception as java.io.IOException: Server returned HTTP response code: 400
Here is my code:
wcf method:
[OperationContract]
[WebInvoke(Method = "POST", ResponseFormat = WebMessageFormat.Json, UriTemplate = "saveSerialNumbers", BodyStyle = WebMessageBodyStyle.WrappedRequest)]
Dictionary<string, object> saveSerialNumbers(byte[] serialData);
Java Client:
for (int i = 1; i < 100; i++) {
sb.append(String.valueOf(gen()));
}
byte[] bytesEncoded = Base64.encodeBase64(sb.toString().getBytes());
String json = "{\"serialDataByte\":\""+sb.toString()+"\"}";
This is my postrequest method:
public String getResultPOST(String jsonObject,String uri,String method) throws Exception{
try {
URL url = new URL(uri+method);
System.out.println(url.toString());
URLConnection connection = url.openConnection();
connection.setDoOutput(true);
connection.setRequestProperty("Content-Type", "application/json");
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
OutputStreamWriter out;
try {
out = new OutputStreamWriter(connection.getOutputStream());
out.write(jsonObject);
out.close();
} catch (Exception e) {
/
// TODO Auto-generated catch block
e.printStackTrace();
}
String line = "";
StringBuilder builder = new StringBuilder();
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
while ((line = in.readLine()) != null) {
builder.append(line);
}
in.close();
return builder.toString();
} catch (Exception e) {
throw e;
//here is the exception
}
}
Here is my method call:
String json = "{\"serialData\":\""+ new String(bytesEncoded) +"\",\"guProductID\":\""+guProductID+"\",\"guStoreID\":\""+guStoreID+"\",\"securityToken\":\""+SecurityToken+"\"}";
String serialContract = serialClient.getResultPOST(json, "http://localhost:3361/EcoService.svc/Json/", "saveSerialNumbers");
Below, there's a simple working prototype to generate json from a string instance. Use this code snippet to update your client part. And it should work.
import java.util.Base64;
public class Test {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();
// composing string to be encoded
sb.append("Part 1 of some text to be encoded to base64 format\n");
sb.append("Part 2 of some text to be encoded to base64 format\n");
sb.append("Part 3 of some text to be encoded to base64 format");
// getting base64 encoded string bytes
byte[] bytesEncoded = Base64.getEncoder().encode(sb.toString().getBytes());
// composing json
String json = "{\"serialDataByte\":\""+ new String(bytesEncoded) +"\"}";
System.out.println(json);
}
}
UPDATE:
The code uses Java 8 SDK. If you are using pre-Java8 version, then consider Apache Commons Codec for this task.
Below there's a sample code, that uses Apache Commons Codec for Base64 encoding (please note that import directive has been changed):
import org.apache.commons.codec.binary.Base64;
public class Test {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();
// composing string to be encoded
sb.append("Part 1 of some text to be encoded to base64 format\n");
sb.append("Part 2 of some text to be encoded to base64 format\n");
sb.append("Part 3 of some text to be encoded to base64 format");
// getting base64 encoded string bytes
byte[] bytesEncoded = Base64.encodeBase64(sb.toString().getBytes());
// composing json
String json = "{\"serialDataByte\":\""+ new String(bytesEncoded) +"\"}";
System.out.println(json);
}
}
UPDATE 2:
Upon sending POST requests make sure that you have marked your request as a POST request. Do not forget this line of code, before making the request:
connection.setRequestMethod("POST");
and use HttpURLConnection instead of URLConnection:
import java.net.HttpURLConnection;

Categories

Resources