Upload to google drive using The files get uploaded, but, only in the root and are always named "Untitled" - google-api

I am trying to upload files to Google drive using a REST API
Everything is working fine, but files are uploading into Google Drive only in the root, and with "Untitled" as their name.
public class UploadTODrive {
public static DriveFiles UploadFileTODrive(String accessToken, MultipartFile files) throws IOException {
RestTemplate restTemplate = new RestTemplate();
String requestUri = "https://www.googleapis.com/upload/drive/v2/files";
System.out.println("ContentType==============: " + files.getContentType());
byte[] s=files.getBytes();
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", files.getContentType());
headers.setContentLength(0);
//headers.setContentType(MediaType.MULTIPART_FORM_DATA);
headers.add("Authorization", "Bearer " + accessToken);
HttpEntity<byte[]> requestEntity = new HttpEntity<>(s, headers);
ResponseEntity<String> response = restTemplate.exchange(requestUri, HttpMethod.POST, requestEntity,
String.class);
System.out.println("=====>Response: " + response);
Gson gson = new Gson();
DriveFiles driveFiles = gson.fromJson(response.getBody(), DriveFiles.class);
return driveFiles;
}
}

Files are uploaded to google drive in two parts.
The first part is the meta data of the file, this being the name and the mimetype most often and occasionally containing a parent directory.
The second part is the upload of the actual file stream.
It seams that you are uploading the file stream but you have forgotten to post the file metadata. This is posted in the body of your request
This is your HTTP Post request. You need to figure out how to add a post body to this. This is the documentation link to the writable fields that you can post insert#request-body
ResponseEntity<String> response = restTemplate.exchange(requestUri, HttpMethod.POST, requestEntity,
String.class);
Update
found this with a bit of googling
RESTRequest1.AddBody('{"title": "Capture.jpg"}', TRESTContentType.ctAPPLICATION_JSON);

Related

How to read response with Content-Type text/plain;charset=UTF-8 using RestTemplate

I have an API (/get-sas-token) that is returning a response with Content-Type=text/plain;charset=UTF-8.
In Postman when I hit this API it returns as sig=PAd%2By7yzue0G%2FVeMKbwvR%2F%2B5a3X8CUTablCIhS3uCuk%3D&s
The code for this API is
#GetMapping("/get-sas-token/{containerName}")
public String getSASToken(#PathVariable("containerName") String containerName)
throws InvalidKeyException, URISyntaxException, StorageException {
CloudBlobContainer appcontainer = config.blobClient().getContainerReference(containerName);
return appConfiguration.generateSASToken(appcontainer);
}
My other microservice is trying to call this RestAPI using RestTemplate via a get request. The response I'm getting in Postman has instead of special characters like �������{J�J��t�\b�`$ؐ#������iG# some weird stuff. So I guess somethings wrong with the character encoding.
P.S : If I remove the Accept-Encoding(gzip, deflate, br) header from Postman it works.How can i get it working in my code
ResponseEntity<String> imageUrl = restTemplate.exchange(
fileServiceUrl + "/get-sas-token/" + containerReference, HttpMethod.GET, request, String.class);
logger.info("imageUrl body-------->>" + imageUrl.getBody());
It prints
imageUrl body-------->>?`I?%&/m?{J..................
I have tried all the possible soultions but nothing works for me
tried this::
template.getMessageConverters()
.add(0, new StringHttpMessageConverter(Charset.forName("UTF-8")));
ResponseEntity<Object> response = template.exchange(endpoint, method, entity,
Object.class);
Also this::
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.set("Accept", "application/json");

Java S3 upload using Spring RestTemplate

I want to make this call using SpringBoot RestTemplate to upload a file to a S3 bucket: https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html
PUT /my-image.jpg HTTP/1.1
Host: myBucket.s3.<Region>.amazonaws.com
Date: Wed, 12 Oct 2009 17:50:00 GMT
Authorization: authorization string
Content-Type: text/plain
Content-Length: 11434
x-amz-meta-author: Janet
Expect: 100-continue
[11434 bytes of object data]
and
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.rootUri("")
.additionalInterceptors((request, body, execution) -> {
request.getHeaders().add("Authorization",
"Bearer a0d78d7922f333ee22d75bea53d01hhkjk83f5ac03f11ccd87787");
return execution.execute(request, body);
}).build();
}
I've tried
Resource resource = new ClassPathResource("logback.xml");
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.TEXT_PLAIN);
HttpEntity<byte[]> requestEntity
= new HttpEntity<>(StreamUtils.copyToByteArray(resource.getInputStream()), headers);
Map<String, Object> parameters = new HashMap<>(4);
parameters.put("cors_enabled", true);
parameters.put("acl", "private");
parameters.put("key", "my-key");
parameters.put("Bucket", "parameters.put("Bucket", "https://cloud.linode.com/object-storage/buckets/eu-central-1/my-bucket-2020");");
restTemplate.put("https://api.linode.com/v4/object-storage/buckets", requestEntity, parameters);
but I got
org.springframework.web.client.HttpClientErrorException$MethodNotAllowed: 405 METHOD NOT ALLOWED: [{"errors": [{"reason": "Method Not Allowed"}]}]
also when Getting I have a problem:
MultiValueMap<String, Object> body
= new LinkedMultiValueMap<>();
UriComponentsBuilder builder =
UriComponentsBuilder.fromHttpUrl("https://api.linode.com/v4/object-storage/buckets/eu-central-1/my-bucket-2020/object-url");
builder.queryParam("method", "GET");
builder.queryParam("name", "43f959d9-a11a-4f2cec88fd7e.JPG");
body.add("method", "GET");
body.add("name", "43f959d9-a11a-4f2cec88fd7e.JPG");
HttpHeaders headers = new HttpHeaders();
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
restTemplate.postForEntity(builder.build().encode().toUri(),
requestEntity, LinodeResponse.class);
and the response:
org.springframework.web.client.HttpClientErrorException$BadRequest: 400 BAD REQUEST: [{"errors": [{"reason": "name is required", "field": "name"}, {"reason": "method is required", "field": "method"}]}]
ans when accessing with AWS-SDK I have this error:
com.amazonaws.services.s3.model.AmazonS3Exception: The AWS Access Key Id you provided does not exist in our records.
Linode seems to offer an API to generate presigned urls for interact with objects in S3.
To use the API, first, you can create two POJO that represent the request and response we will send and receive from the API so we can use to serialize an deserialize JSON information.
For the request object:
public class LinodeGeneratePresignedUrlRequest {
private String method;
private String name;
#JsonProperty("content_type")
private String contentType;
#JsonProperty("expires_in")
private int expiresIn;
// Getters and setters
}
And for the response:
pubic class LinodeGeneratePresignedUrlResponse {
private String url;
// Getters and setters
}
These objects match the information required by the endpoint.
If you want to create an object in your bucket with the Linode API, you first need to request a presigned URL. Once obtained, you will use this URL to perform the actual operation over the bucket object. The operation is defined by the method parameter passed to the API. Consider the following example:
// Obtain a reference to the RestTemplate instance.
// It should support the interchange of JSON information
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
// Set content type to the one required by the Linode API application/json
headers.setContentType(MediaType.APPLICATION_JSON);
// Set the appropriate credentials for the Linode API
String token = "your token";
headers.set(HttpHeaders.AUTHORIZATION, "Bearer" + token);
// Create the presigned url request
LinodeGeneratePresignedUrlRequest linodeGeneratePresignedUrlRequest =
new LinodeGeneratePresignedUrlRequest();
// Operation to perform when you interact with AWS later
// In this case, PUT because you need to create a new object
linodeGeneratePresignedUrlRequest.setMethod("PUT");
// The object name: can match or not the actual file you want to upload
linodeGeneratePresignedUrlRequest.setName("my-object-name.pdf");
// As you are performing an upload (PUT, POST), indicate the content type of
// the information you are uploading to AWS. It should match the provided later
// when you interact with AWS. For instance, consider that you are uploading a PDF file
linodeGeneratePresignedUrlRequest.setContentType("application/pdf");
// Optionally, you can set the expiration time of the generated presigned url
// By default, an hour (3600 seconds)
// Perform the actual Linode API invocation
HttpEntity<LinodeGeneratePresignedUrlRequest> requestEntity =
new HttpEntity<LinodeGeneratePresignedUrlRequest>(linodeGeneratePresignedUrlRequest, headers);
// The Linode API URL for your cluster and bucket
String linodeApiUrl = "https://api.linode.com/v4/object-storage/buckets/eu-central-1/my-bucket-2020/object-url";
HttpEntity<LinodeGeneratePresignedUrlResponse> responseEntity = restTemplate.exchange(linodeApiUrl, HttpMethod.POST, requestEntity, LinodeGeneratePresignedUrlResponse.class);
// Linde wil provide a response with a property named 'url' corresponding
// to the presigned url that we can use to interact with AWS S3
LinodeGeneratePresignedUrlResponse linodeGeneratePresignedUrlResponse = responseEntity.getBody();
String signedUrl = linodeGeneratePresignedUrlResponse.getUrl();
// Now, send the actual file.
// I am following the example provided in the AWS documentation:
// https://docs.aws.amazon.com/AmazonS3/latest/dev/PresignedUrlUploadObjectJavaSDK.html adapt for RestTemplate
HttpHeaders headersForS3 = new HttpHeaders();
// You should provide the same content type you indicated previously
headersForS3.set("Content-Type", "application/pdf");
Resource resource = new FileSystemResource("my-object-name.pdf");
HttpEntity<byte[]> requestEntityForS3 =
new HttpEntity<>(
StreamUtils.copyToByteArray(resource.getInputStream()), headersForS3);
// You should use the same HTTP verb as indicated in
// the 'method' parameter before
restTemplate.exchange(signedUrl, HttpMethod.PUT, requestEntityForS3, Void.class);
The process for retrieving the object created is very similar:
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
String token = "your token";
headers.set(HttpHeaders.AUTHORIZATION, "Bearer" + token);
LinodeGeneratePresignedUrlRequest linodeGeneratePresignedUrlRequest =
new LinodeGeneratePresignedUrlRequest();
// Instead of PUT, indicate that you want to retrieve the object
linodeGeneratePresignedUrlRequest.setMethod("GET");
// your object name
linodeGeneratePresignedUrlRequest.setName("my-object-name.pdf");
HttpEntity<LinodeGeneratePresignedUrlRequest> requestEntity =
new HttpEntity<LinodeGeneratePresignedUrlRequest>(linodeGeneratePresignedUrlRequest, headers);
String linodeApiUrl = "https://api.linode.com/v4/object-storage/buckets/eu-central-1/my-bucket-2020/object-url";
HttpEntity<LinodeGeneratePresignedUrlResponse> responseEntity = restTemplate.exchange(linodeApiUrl, HttpMethod.POST, requestEntity, LinodeGeneratePresignedUrlResponse.class);
LinodeGeneratePresignedUrlResponse linodeGeneratePresignedUrlResponse = responseEntity.getBody();
String signedUrl = linodeGeneratePresignedUrlResponse.getUrl();
// Read the object from your bucket
byte[] objectBytes = restTemplate.getForObject(signedUrl, byte[].class);
// And use the information as you need
Files.write(Paths.get("my-object-name.pdf"), objectBytes);
Of course, if Linode provides you the appropriate credentials, you can also use the AWS SDK to interact with S3 directly.
Spring equivalent of the cURL command you've provided can be:
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
String token = "";
headers.set(HttpHeaders.AUTHORIZATION, token);
JSONObject data = new JSONObject();
data.put("cors_enabled", true);
data.put("acl", "private");
HttpEntity<String> requestEntity = new HttpEntity<String>(data.toString(), headers);
String url = "https://api.linode.com/v4/object-storage/buckets/eu-central-1/bonansa15122020/access";
HttpEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.PUT, requestEntity, String.class);
In your first example, you've haven't provided Authorization header, so you're getting a 401 response. You're aren't using the RestTemplate you'd created using RestTemplateBuilder here.
In your second example, it seems the request body isn't a JSON (you're reading the logback file, so highly unlikely there's a JSON inside it). It seems the Linode API expects a JSON body.
Update:
I believe you can use PUT request as part of POST request to endpoint https://api.linode.com/v4/object-storage/buckets/{clusterId}/{bucket}/object-url
More details here - https://developers-linode.netlify.app/api/v4/object-storage-buckets-cluster-id-bucket-object-url#post
I won't be able to test because I don't have account with linode.
I think another viable solution would be to use aws sdk for s3 to upload files to the linode endpoint.
Here is the simple example -
https://github.com/awsdocs/aws-doc-sdk-examples/blob/master/java/example_code/s3/src/main/java/aws/example/s3/PutObject.java
Original:
Based on the linode api documentation the method should be post.
https://www.linode.com/docs/api/object-storage/#object-storage-bucket-create
restTemplate.post("https://api.linode.com/v4/object-storage/buckets", requestEntity, parameters)
Also please review the request body to conform to documentation.

Sending a multipart request using RestTemplate

I want to make a multipart request to some external API (created using Spring Boot) but all I get is Required request part 'file' is not present.
I know the source code of the external API but I can't modify it. It looks like this:
#PostMapping("/upload")
public ResponseEntity handleFileUpload(#RequestParam("file") MultipartFile file){
return ResponseEntity.ok().build();
}
And from my application I create and send requests exactly like on the following snippet:
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
MultiValueMap<String, Object> body
= new LinkedMultiValueMap<>();
body.add("file", "dupa".getBytes());
HttpEntity<MultiValueMap<String, Object>> requestEntity
= new HttpEntity<>(body, headers);
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate
.postForEntity("http://api:8080/upload", requestEntity, String.class);
return response.getBody();
What's the reason it doesn't work? The above code rewritten using Apache HttpClient works like charm.
You basically have two options, the solution with byte array:
map.add("file", new ByteArrayResource(byteArrayContent) {
#Override
public String getFilename() {
return "yourFilename";
}
});
I remember having a problem with just adding a byte array, so you need to have a filename too and use ByteArrayResource.
Or adding a File:
map.add("file", new FileSystemResource(file));

uploading multipart file to another server

I'm working on a spring boot / Angular 6 application, I want to upload files to a server, I followed this tutorial to upload a multipart file :" https://grokonez.com/spring-framework/spring-boot/angular-5-upload-get-multipartfile-to-from-spring-boot-server ". The upload of the file is on a folder in the application but now I want to upload the files to another server with URL; f.e : localhost:8081/uploads : it's another server, how can I do that?
You should do this with spring rest template and construct the body as below
MultiValueMap<String, Object> body
= new LinkedMultiValueMap<>();
body.add("files", getTestFile());
body.add("files", getTestFile());
body.add("files", getTestFile());
HttpEntity<MultiValueMap<String, Object>> requestEntity
= new HttpEntity<>(body, headers);
String serverUrl = "http://localhost:8081/upload/";
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate
.postForEntity(serverUrl, requestEntity, String.class);

Spring RestTemplate receives "401 Unauthorized"

I am using the following to retrieve JSON via RestTemplate in Spring 4:
protected DocInfoResponse retrieveData(String urlWithAuth) {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Basic " + auth.getSig());
HttpEntity<String> request = new HttpEntity<String>(headers);
ResponseEntity<DocInfoResponse> response = restTemplate.exchange(urlWithAuth, HttpMethod.GET, request, DocInfoResponse.class);
return response.getBody();
}
I used the same code (with different response class) to successfully get a JSON doc from the same site (with different parameters to get a different doc).
When I execute the above code I receive the following stack trace (in part):
Caused by: org.springframework.web.client.HttpClientErrorException: 401 Unauthorized
at
org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:91) ~[spring-web-4.3.7.RELEASE.jar:4.3.7.RELEASE]
Can anyone point me to why this might be receiving the exception?
I found that my issue originally posted above was due to double encryption happening on the auth params. I resolved it by using UriComponentsBuilder and explicitly calling encode() on the the exchange().
SyncResponse retrieveData(UriComponentsBuilder builder) {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.set("Accept", MediaType.APPLICATION_JSON_VALUE);
HttpEntity<String> request = new HttpEntity<String>(headers);
ResponseEntity<SyncResponse> response = restTemplate.exchange(builder.build().encode().toUri(), HttpMethod.GET, request, SyncResponse.class);
return response.getBody();
}
My UriComponentsBuilder was built using:
UriComponentsBuilder buildUrl(String urlString) {
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(urlString);
return auth.appendAuth(builder);
}
(The auth.appendAuth() adds additional .queryParams() needed by the target service in urlString.)
The call to execute this was retrieveData(buildUrl(urlString));.
After investigating on my own problem, I realized that FireFox RESTClient was successful because I was connected to the target URL. The Basic Auth I thought I was using, was not so basic after all.
Eventually, I read the doc of the app i was trying to connect to and realized they propose a connection token mechanism. Now it works.
After reading your code, I say it looks quite OK, although I'm not sure what is your object auth on which you call getSig.
First things first: try to access your service from any client, like a web browser, a PostMan or RESTClient. Make sure you successfully retrieve your infos WITHOUT being connected to your app!!!
Depending on the result, I say you should, either try to encrypt manually your Authorization token (you'll easilly find posts on this site to show you how to) or try another connection mechanism.
The process of creating the Authorization header is relatively straightforward for Basic Authentication, so it can pretty much be done manually with a few lines of code:
HttpHeaders createHeaders(String username, String password){
return new HttpHeaders() {{
String auth = username + ":" + password;
byte[] encodedAuth = Base64.encodeBase64(
auth.getBytes(Charset.forName("US-ASCII")) );
String authHeader = "Basic " + new String( encodedAuth );
set( "Authorization", authHeader );
}};
}
Then, sending a request becomes just as simple:
RestTemplate restTemplate = new RestTemplate();
restTemplate.exchange
(uri, HttpMethod.POST, new HttpEntity<T>(createHeaders(username, password)), clazz);
https://www.baeldung.com/how-to-use-resttemplate-with-basic-authentication-in-spring#manual_auth

Resources