Send byte array and receive String through REST web service - java

in my Spring Rest web service I send a file (even big size) as byte array but when I receive the information, the object is a String so when I make the cast from Object to byte[] I receive the following error:
java.lang.ClassCastException: java.lang.String cannot be cast to [B
The originl file is converted through
Files.readAllBytes(Paths.get(path))
and this byte[] is filled in one object with a field result of Object type.
When the Client retrieve this object and it gets result class with cast to byte[] it appears the above exception, this is the client code
Files.write(Paths.get("test.txt"),((byte[])response.getResult()));
If I use a cast to string and then to bytes the content of the file is different from original file. I don't care the file type, file content, I only have to copy from server to client directory
How can I do?Thanks
server class:
#Override
#RequestMapping(value = "/", method = RequestMethod.GET)
public #ResponseBody Response getAcquisition(#RequestParam(value="path", defaultValue="/home") String path){
try {
byte[] file = matlabClientServices.getFile(path);
if (file!=null){
FileTransfer fileTransfer= new FileTransfer(file, Paths.get(path).getFileName().toString());
return new Response(true, true, fileTransfer, null);
}
else
return new Response(false, false, "File doesn't exist!", null);
} catch (Exception e) {
ErrorResponse errorResponse= ErrorResponseBuilder.buildErrorResponse(e);
LOG.error("Threw exception in MatlabClientControllerImpl::getAcquisition :" + errorResponse.getStacktrace());
return new Response(false, false, "Error during file retrieving!", errorResponse);
}
}
and FileTransfer is:
public class FileTransfer {
private byte[] content;
private String name;
..get and set
client class:
#RequestMapping(value = "/", method = RequestMethod.GET)
public #ResponseBody Response getFile(#RequestParam(value="path", defaultValue="/home") String path){
RestTemplate restTemplate = new RestTemplate();
Response response = restTemplate.getForObject("http://localhost:8086/ATS/client/file/?path={path}", Response.class, path);
if (response.isStatus() && response.isSuccess()){
try {
#SuppressWarnings("unchecked")
LinkedHashMap<String,String> result= (LinkedHashMap<String,String>)response.getResult();
//byte[] parseBase64Binary = DatatypeConverter.parseBase64Binary((String)fileTransfer.getContent());
Files.write(Paths.get(result.get("name")), DatatypeConverter.parseBase64Binary(result.get("content")));
return new Response(true, true, "Your file has been written!", null);
} catch (IOException e) {
return new Response(true, true, "Error writing your file!!", null);
}
}
return response;
}

So the client should be something like this
#RequestMapping(value = "/test/", method = RequestMethod.GET)
public Response getFileTest(#RequestParam(value="path", defaultValue="/home") String path){
RestTemplate restTemplate = new RestTemplate();
Response response = restTemplate.getForObject("http://localhost:8086/ATS/client/file/?path={path}", Response.class, path);
if (response.isStatus() && response.isSuccess()){
try {
byte[] parseBase64Binary = DatatypeConverter.parseBase64Binary((String)response.getResult());
Files.write(Paths.get("test.txt"),parseBase64Binary );
} catch (IOException e) {
}
}
return response;
}

I believe the content-type here is text/plain, therefore the content of the file is a plain text. Simply generate byte array from the response:
Files.write(Paths.get("test.txt"),((String)response.getResult()).getBytes());

Related

Return file from Google Cloud Storage through REST API without downloading locally first

We would like to have a Java REST API to return files from Google Cloud Storage as attachment. I was able to able to get it to work using the following method. The problem is that the file has to be downloaded locally to the service container (we are deploying on Google Cloud Run) and this is a problem in the case of very large files, and may generally be bad practice. Is there a way to modify this code somehow to skip the creation of a local file?
#GetMapping(path = "/file", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public ResponseEntity<InputStreamResource> getSpecificFile(#RequestParam String fileName,
#RequestParam String bucketName, #RequestParam String projectName) {
Storage storage = StorageOptions.newBuilder().setProjectId(projectId).build().getService();
Blob blob = storage.get(bucketName, fileName);
ReadChannel readChannel = blob.reader();
String outputFileName = tempFileDestination.concat("\\").concat(fileName);
try (FileOutputStream fileOutputStream = new FileOutputStream(outputFileName)) {
fileOutputStream.getChannel().transferFrom(readChannel, 0, Long.MAX_VALUE);
String contentType = Files.probeContentType(Paths.get(outputFileName));
FileInputStream fileInputStream = new FileInputStream(outputFileName);
return ResponseEntity.ok().contentType(MediaType.valueOf(contentType))
.header("Content-Disposition", "attachment; filename=" + fileName)
.body(new InputStreamResource(fileInputStream));
} catch (IOException e) {
e.printStackTrace();
return ResponseEntity.internalServerError().body(null);
} finally {
// delete the local file as cleanup
try {
Files.delete(Paths.get(outputFileName));
} catch (IOException e) {
e.printStackTrace();
}
}
}
Well, that did not take me long to figure out. I was able to make it work as follows:
#GetMapping(path = "/file", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public ResponseEntity<InputStreamResource> getSpecificFile(#RequestParam String fileName, #RequestParam String bucketName, #RequestParam String projectName) {
Storage storage = StorageOptions.newBuilder().setProjectId(projectId).build().getService();
Blob blob = storage.get(bucketName, fileName);
ReadChannel readChannel = blob.reader();
try {
String contentType = Files.probeContentType(Paths.get(fileName));
InputStream inputStream = Channels.newInputStream(readChannel);
return ResponseEntity.ok().contentType(MediaType.valueOf(contentType))
.header("Content-Disposition", "attachment; filename=" + fileName)
.body(new InputStreamResource(inputStream));
} catch (IOException e) {
e.printStackTrace();
return ResponseEntity.internalServerError().body(null);
}
}
Basically redirect the InputStream to the readChannel instead of the file.

Modify/Replace ClientHttpResponse body inside interceptor (ClientHttpRequestInterceptor)

I'm adding message level encryption (MLE) to an existing code base for outgoing requests. To do this, I simply wrote an interceptor that will catch outgoing requests, encrypt their bodies, and then send the request out. The response we get is also encrypted, and must be decrypted. This all is working fine for me. The only problem I'm having is that I must replace the ClientHttpResponse encrypted body with the now decrypted JSON. How can I do this? I don't see any methods that will let me alter the response body. Thanks in advance.
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
throws IOException {
ClientHttpResponse response;
String bodyStr = new String(body);
// Encrypt the body and send
bodyStr = encrypt(bodyStr);
try {
response = execution.execute(request, bodyStr.getBytes());
} catch (Exception e) {
throw e;
}
// Decrypt the response body
String decryptedResponseBody = decrypt(response.getBody());
// Set the response body to the decrypted data (JSON)
// response.setBody(decryptedResponseBody)?????????
return response;
}
You will need to create an implementation of ClientHttpResponse which is not too hard since there are only a few methods to override, I added an example of how you would fix this. I hope this helps. I would suggest adding a named bean for this type of request, you don't want to have all your resttemplates being encrypted/decrypted.
restTemplate.getInterceptors().add( (ClientHttpRequestInterceptor)
(request, body, execution) -> {
ClientHttpResponse response;
String bodyStr = new String(body);
// Encrypt the body and send
bodyStr = encrypt(bodyStr);
try {
response = execution.execute(request, bodyStr.getBytes());
String text = IOUtils.toString(response.getBody(), StandardCharsets.UTF_8.name());
// Decrypt the response body
String decryptedResponseBody = decrypt(text);
} catch (Exception e) {
throw e;
}
InputStream inputStream = inputStream = new ByteArrayInputStream(decryptedResponseBody.getBytes());
return new ClientHttpResponse() {
#Override
public HttpHeaders getHeaders() {
return response.getHeaders();
}
#Override
public InputStream getBody() throws IOException {
return inputStream;
}
#Override
public HttpStatus getStatusCode() throws IOException {
return response.getStatusCode();
}
#Override
public int getRawStatusCode() throws IOException {
return response.getRawStatusCode();
}
#Override
public String getStatusText() throws IOException {
return response.getStatusText();
}
#Override
public void close() {
response.close();
}
};
}))

Spring Framework: Byte Array is corrupted when received

I want to create a file upload system that works with byte arrays using Spring Framework. I have a controller as below:
#Controller
public class FileUploadController {
#Autowired
FileUploadService fileService;
#GetMapping("/")
public void index() {
System.out.println("Show Upload Page");
}
#PostMapping("/")
public void uploadFile(#RequestParam("file") byte[] file, #RequestParam("fileName")String fileName, #RequestParam("fileType") String fileType, RedirectAttributes redirectAttributes) {
try {
HashMap<String, String> result = fileService.saveFile(file,fileName,fileType);
String filePath = result.get("filePath");
String fileSize = result.get("fileSize");
System.out.println("Path " + filePath + " " + fileSize + " Bytes");
} catch (Exception e) {
e.printStackTrace();
}
}
}
and a service like so:
#Service
public class FileUploadService {
#Value("${app.upload.dir:${user.home}}")
public String uploadDir;
public HashMap<String, String> saveFile(byte[] file, String fileName, String fileType) throws Exception {
try {
Path copyLocation = Paths
.get(uploadDir + File.separator + StringUtils.cleanPath(fileName));
String pathString = copyLocation.toString();
FileOutputStream stream = new FileOutputStream(pathString);
stream.write(file);
String fileSize = String.valueOf(Files.size(copyLocation));
HashMap<String, String> result = new HashMap<String, String>();
result.put("filePath", pathString);
result.put("fileSize", fileSize);
return result;
} catch (Exception e) {
e.printStackTrace();
throw new Exception("Could not store file " + fileName
+ ". Please try again!");
}
}
}
And I am testing this API using this code which uses Apache HttpClient:
public class app {
public static void main(String[] args) throws IOException, InterruptedException {
byte[] array = Files.readAllBytes(Paths.get("/Users/hemodd/Desktop/test-pic.png"));
CloseableHttpClient client = HttpClients.createDefault();
HttpPost httpPost = new HttpPost("http://localhost:8080");
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.addTextBody("fileName", "Newwwww");
builder.addTextBody("fileType", "png");
builder.addBinaryBody("file", array);
HttpEntity multipart = builder.build();
httpPost.setEntity(multipart);
CloseableHttpResponse response = client.execute(httpPost);
client.close();
}
}
Now, the problem is that the result of the writing of the received byte array is a corrupted file. I don't want to use MultipartFile and I need to stick with byte arrays.
Any help is appreciated.
The problem is with passing file in your controller:
#RequestParam("file") byte[] file
You have several options:
use the multipart file upload:
#RequestParam("file") MultipartFile file
parse byte array from request body:
#RequestBody byte[] file
encode data to the string. In your example, if you want to send bytes as the parameter, you have to encode your binary data so it can be sent as a string (e.g. using Base64). And then decode on the server-side.
Your test app creates and sends a multipart request. For sending byte array in body you have to change it to:
HttpEntity entity = new ByteArrayEntity(array);
httpPost.setEntity(entity);

Getting blank response while uplaoding large PDF file from Android app

I want to upload PDF file from android native application to server. I have checked the server configuration and everything is good like upload time and size. I am converting PDF file in base64 and trying to send to server. Server sending blank value of FILE[] (check the response below).
I have tried another method to upload PDF i.e multipart. This is also not working.
//Multipart code - 1st method
RequestBody requestBody = new MultipartBody.Builder().setType(MultipartBody.FORM).addFormDataPart("file", new File(pdfpath).getName(),RequestBody.create(MediaType.parse("application/pdf"), new File(pdfpath).getName())).addFormDataPart("some-field", "some-value").build();
System.out.println("Multipart Data "+requestBody.toString());
//---------------------------------------------------------
//Base64 code - 2nd method
public String converPDFBase64(File mfile) {
ByteArrayOutputStream output=null;
try {
InputStream inputStream = null;
inputStream = new FileInputStream(mfile.getAbsolutePath());
byte[] buffer = new byte[8192];
int bytesRead;
output = new ByteArrayOutputStream();
Base64OutputStream output64 = new Base64OutputStream(output, Base64.DEFAULT);
while ((bytesRead = inputStream.read(buffer)) != -1) {
output64.write(buffer, 0, bytesRead);
}
output64.close();
} catch (IOException e) {
e.printStackTrace();
} return output.toString();
}
//--------------------------------------------------------------------
//File send to server code
private Message getMessageObjectForMedia(String type,String media){
HashMap<String, Object> input = new HashMap<>();
input.put(RequestParameters.USERID, "" + SharedPreferencesMethod.getUserId(this));
input.put(RequestParameters.TO_USERID, "" + user.getUserId());
input.put(RequestParameters.MESSAGE_MEDIA, "" + media);
input.put(RequestParameters.MESSAGE_MEDIA_TYPE, "" + type);
input.put(RequestParameters.MESSAGE, "" +"PDF");
String value = System.currentTimeMillis() + "";
input.put("msg_identifer", value);
System.out.println("Input Type "+input);
API.sendRequestToServerPOST_PARAM(this, API.SEND_MESSAGE, input);
// service call for account verification
Message messageObj = new Message();
messageObj.setId(REMOVE);
messageObj.setMsg_identifer(value);
messageObj.setMessageMedia(String.valueOf(media));
messageObj.setMessageMediaType(type);
messageObj.setReadStatus("0");
messageObj.setMessageAT("Sending...");
messageObj.setUserId(SharedPreferencesMethod.getUserId(getApplicationContext()));
return messageObj;
}
//request send to server
{to_user=577, mediaType=pdf, media=okhttp3.MultipartBody$Builder#dfb1c38, message=PDF, userid=738, msg_identifer=1564994083868}
//response from server
{"success":"success","_POST{"to_user":"577","mediaType":"pdf","media":"okhttp3.MultipartBody$Builder#dfb1c38","message":"PDF","userid":"738","msg_identifer":"1564994083868"},"_FILES":[],"message_info":{"id":"4132","msg_identifer":"1564994083868","referenceId":"0","reply":"PDF","user_id":"738","reply_at":"2019-08-05 14:04:45","message_id":"115","ip_address":"00.00.000.000","read_status":"0","delStatusUserFrom":"0","delStatusUserTo":"0","media":"","mediaType":"","reference":{},"messagedAT":"Today at 2:04PM","message_date":"05-08-2019","message_time":"2:04 PM"},"RP_MESSAGE":"ALL_OKAY"}
//in response _FILES:[] is blank
//Expected Response
{
"success": "success",
"_POST": {
"userid": "577",
"to_user": "594",
"message": "Hello",
"mediaType": "pdf"
},
"_FILES": {
"media": {
"name": "Comics activity pack.pdf",
"type": "application/pdf",
"tmp_name": "/tmp/phpYR7O4q",
"error": 0,
"size": 2149146
}
}
}
I want to send large PDF file to server by using any method.
I got it, I was using AQuery and now I have changed it. Now calling service by OkHttpClient and it's working fine.
public Message getMessageMedia(File file,String type) {
Message messageObj = new Message();
try {
OkHttpClient client = new OkHttpClient.Builder().connectTimeout(10, TimeUnit.SECONDS).writeTimeout(180, TimeUnit.SECONDS).readTimeout(180, TimeUnit.SECONDS).build();
RequestBody body = new MultipartBody.Builder().setType(MultipartBody.FORM)
.addFormDataPart(RequestParameters.USERID, "" + SharedPreferencesMethod.getUserId(this))
.addFormDataPart(RequestParameters.TO_USERID, "" + user.getUserId())
.addFormDataPart(RequestParameters.MESSAGE_MEDIA,file.getName(),RequestBody.create(MediaType.parse("application/pdf"),file))
.addFormDataPart(RequestParameters.MESSAGE_MEDIA_TYPE, "" + type)
.addFormDataPart(RequestParameters.MESSAGE,"" +"PDF")
.addFormDataPart("msg_identifer", System.currentTimeMillis() + "")
.build();
Request request = new Request.Builder()
.url(API.SEND_MESSAGE)
.post(body)
.build();
System.out.println("Input Data"+request.toString());
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(final Call call, final IOException e) {
System.out.println("PDF Faliure "+e.toString());
}
#Override
public void onResponse(final Call call, final Response response) throws IOException {
if (!response.isSuccessful()) {
System.out.println("PDF Response Error "+response.toString());
}else{
System.out.println("PDF Response "+response.toString());
}
}
});
etMessage.setText("PDF");
Date now = Calendar.getInstance().getTime();
SimpleDateFormat dateFormat = new SimpleDateFormat("hh:mm a");
messageObj.setId(REMOVE);
messageObj.setMsg_identifer(System.currentTimeMillis() + "");
messageObj.setMessageMedia(String.valueOf(file));
messageObj.setMessageMediaType(type);
messageObj.setReadStatus("0");
messageObj.setMessageAT("Sending...");
messageObj.setUserId(SharedPreferencesMethod.getUserId(getApplicationContext()));
} catch (Exception ex) {
System.out.println("Media Message Error "+ex);
}
return messageObj;
}

400 Bad Request, RESTTEMPLATE

Im trying to send restTemplate with long and image data. Im following this tutorial code and getting next error:
W/RestTemplate: POST request for "http://192.168.0.250:8081/server/upload" resulted in 400 (Bad Request); invoking error handler
E/ContentValues: 400 Bad Request org.springframework.web.client.HttpClientErrorException: 400 Bad Request
I have made simply restteamplate for Long data, but here I have some trubles.
Here are my Android client:
protected AnotherPostDTO doInBackground(Void... params) {
Resource resource = new ClassPathResource("res/drawable/bbb.png");
formData = new LinkedMultiValueMap<String, Object>();
formData.add("owners_id", "1");
formData.add("file", resource);
try {
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<MultiValueMap<String, Object>>(formData, requestHeaders);
ArrayList<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>(Arrays.asList(new MappingJackson2HttpMessageConverter(), new FormHttpMessageConverter()));
RestTemplate restTemplate = new RestTemplate(converters);
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
restTemplate.getMessageConverters().add(new FormHttpMessageConverter());
return restTemplate.postForObject(Constants.URLs.UPLOAD_FILE, requestEntity, AnotherPostDTO.class);
} catch (Exception e) {
Log.e(TAG, e.getMessage(), e);
}
return post;}
My server controller:
#RequestMapping(value = "postformdata", method = RequestMethod.POST, headers = "Content-Type=multipart/form-data")
public #ResponseBody String handleFormUpload(#RequestParam("description") String description,
#RequestParam("file") MultipartFile file) {
if (!file.isEmpty()) {
byte[] bytes = null;
try {
bytes = file.getBytes();
} catch (IOException e) {
System.out.println("taski");
}
return "file upload received! Name:[" + description + "] Size:["
+ bytes.length + "]";
} else {
return "file upload failed!";
}
}
and bean:
#Bean
MultipartConfigElement multipartConfigElement() {
MultipartConfigFactory factory = new MultipartConfigFactory();
factory.setMaxFileSize("128KB");
factory.setMaxRequestSize("128KB");
return factory.createMultipartConfig();
}
Any ideas?

Categories