Uploading an image to Amazon S3 - android

I need to upload a bitmap to Amazon S3. Below is the code I have so far, built after going through the docs and sample code.
public class AmazonS3Test {
private static final String TAG = "MyApp.AmazonS3Stuff";
private static AmazonS3 mS3 = null;
private static final String mS3BucketName = "bucketname";
private static BasicAWSCredentials mCredentials = new BasicAWSCredentials(".....", "....");
private static void uploadImageToAmazonS3(String key, File file) {
PutObjectRequest request = new PutObjectRequest(mS3BucketName, key, file);
try {
PutObjectResult result = getS3Instance().putObject(request);
} catch (AmazonClientException e) {
Log.e(TAG, "Amazon exception uploading the image to Amazon S3 " + key + " " + mS3BucketName, e);
} catch (Exception e) {
Log.e(TAG, "Exception uploading the image to Amazon S3 " + key + " " + mS3BucketName, e);
}
// TODO Handle result
}
private static AmazonS3 getS3Instance() {
if (mS3 == null) {
mS3 = new AmazonS3Client(mCredentials); // <---- Exception here
}
return mS3;
}
}
Stack trace:
Caused by: java.lang.NoSuchMethodError: org.apache.commons.httpclient.params.i.a
at com.amazonaws.http.HttpClient.<init>(Unknown Source)
at com.amazonaws.AmazonWebServiceClient.<init>(Unknown Source)
at com.amazonaws.services.s3.AmazonS3Client.<init>(Unknown Source)
at com.amazonaws.services.s3.AmazonS3Client.<init>(Unknown Source)
at com.addapps.taxiapp.utils.Utils.getS3Instance(AmazonS3Test.java)
I've little idea what that exception means, or why I cannot get an instance of AmazonS3.
I'm really struggling with S3, so if anyone can point me to any good resources I'd really appreciate it.

This exception was caused by my not having all the required AWS libraries in the project. AWS has a lot of libraries in different forms, and although not clear which are required, it won't work until the right ones are present.

Internally, the Amazon Web Service Client is using Apache commons HttpClient. From the stacktrace, it appears as if you have a library conflict. Check to see if you are using HttpClient somewhere else in your project. Most likely, you are using a different version than the Amazon Web Service Client is expecting.

Related

How to decode SafetyNet JWS response?

I am investigating SafetyNet provided by Google within my Android Application.
To start with I simply called the SafetyNet attest API and Base64 decoded the parts as shown in the Google supplied examples.
SafetyNet.getClient(this).attest(NONCE, <API KEY>)
.addOnSuccessListener(this, new OnSuccessListener<SafetyNetApi.AttestationResponse>() {
#Override
public void onSuccess(final SafetyNetApi.AttestationResponse attestationResponse) {
initialDataExtraction(attestationResponse.getJwsResult());
}
})
.addOnFailureListener(this, new OnFailureListener() {
#Override
public void onFailure(#NonNull final Exception exception) {
if (exception instanceof ApiException) {
final ApiException apiException = (ApiException) exception;
Log.e(TAG, "onFailure: " + apiException.getMessage() + " " + apiException.getStatusCode());
} else {
Log.e(TAG, "Error: ", exception);
}
}
});
I extract the JWS parts as follows:-
private byte[] initialDataExtraction(final String jwsResult) {
final String[] jwsResultParts = jwsResult.split("[.]");
if (jwsResultParts.length == 3) {
final byte[] header = Base64.decode(jwsResultParts[0], Base64.NO_WRAP);
final byte[] data = Base64.decode(jwsResultParts[1], Base64.NO_WRAP);
final byte[] signature = Base64.decode(jwsResultParts[2], Base64.NO_WRAP);
Log.d(TAG, "initialDataExtraction: header = " + new String(header, UTF_8));
Log.d(TAG, "initialDataExtraction: data = " + new String(data, UTF_8));
Log.d(TAG, "initialDataExtraction: signature = " + new String(signature, UTF_8));
return data;
} else {
Log.e(TAG, "initialDataExtraction: Failure: Illegal JWS signature format. The JWS consists of " + jwsResultParts.length + " parts instead of 3.");
return null;
}
}
I am using android.util.Base64 to decode the parts and the majority of the time the decoding completes OK.
Occasionally I receive this exception though:-
java.lang.IllegalArgumentException: bad base-64
at android.util.Base64.decode(Base64.java:161)
at android.util.Base64.decode(Base64.java:136)
at android.util.Base64.decode(Base64.java:118)
when decoding the Signature part.
What am I doing wrong when decoding to see this intermittent error?
I then moved onto to using a JWT library to decode the tokens.
first I tried group: 'com.auth0.android', name: 'jwtdecode', version: '1.1.1'
the code I tried is
final JWT jwt = new JWT(jwsResult);
which consistently fails with the following error
com.auth0.android.jwt.DecodeException: The token's payload had an invalid JSON format.
at com.auth0.android.jwt.JWT.parseJson(JWT.java:235)
at com.auth0.android.jwt.JWT.decode(JWT.java:203)
at com.auth0.android.jwt.JWT.<init>(JWT.java:40)
Caused by: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected a string but was BEGIN_ARRAY at line 1 column 23 path $.
at com.google.gson.Gson.fromJson(Gson.java:899)
at com.google.gson.Gson.fromJson(Gson.java:852)
at com.google.gson.Gson.fromJson(Gson.java:801)
This exception seems to be caused by the Auth0 library being unable to parse headers 4.1.6. "x5c" (X.509 Certificate Chain) Header format which is odd as the JWS Spec clearly states the value is represented by a JSON aray:-
The "x5c" (X.509 Certificate Chain) Header Parameter contains the
X.509 public key certificate or certificate chain [RFC5280]
corresponding to the key used to digitally sign the JWS. The
certificate or certificate chain is represented as a JSON array of
certificate value strings.
However If I copy and paste the same jws result string into a pure java project and use compile 'com.auth0:java-jwt:3.3.0' and use this code:-
String token = "<JWS TOKEN>";
try {
final DecodedJWT jwt = JWT.decode(token);
System.out.println("Header = " + jwt.getHeader());
System.out.println("Payload = " + jwt.getPayload());
System.out.println("Signature = " + jwt.getSignature());
} catch (JWTDecodeException exception){
throw new RuntimeException(exception);
}
The Jws Token is decoded successfully.
What am I doing wrong within my Android application that stops the auth0 android jwt library working as desired?
I then tried 'io.jsonwebtoken:jjwt:0.9.0' library within my Android application.
When I execute this code:-
Jwts.parser().parse(jwsResult).getBody();
it fails with:-
java.lang.IllegalArgumentException: A signing key must be specified if the specified JWT is digitally signed.
at io.jsonwebtoken.lang.Assert.notNull(Assert.java:85)
at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:331)
What signing key do I need to pass to Jwts? The only key I have is my API key held in the Google API Console, is this the key I should employ?
when I pass it as follows:
Jwts.parser().setSigningKey<API KEY>.parse(jwsResult).getBody();
this fails with:-
java.lang.IllegalArgumentException: Key bytes can only be specified for HMAC signatures. Please specify a PublicKey or PrivateKey instance.
at io.jsonwebtoken.lang.Assert.isTrue(Assert.java:38)
at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:324)
What is the correct approach to decode and consume the Jws result received from SafetyNet attest API call?
I discovered a fix for the java.lang.IllegalArgumentException: bad base-64 issue from this question Base64: java.lang.IllegalArgumentException: Illegal character
simply replace characters in jws token before decoding
token.replace('-', '+').replace('_', '/')
I identified this library not only does it do the job it works fine on Android.
// https://mvnrepository.com/artifact/com.nimbusds/nimbus-jose-jwt
implementation group: 'com.nimbusds', name: 'nimbus-jose-jwt', version: '5.1'
try {
final JWSObject jwsObject = JWSObject.parse(jwsResult);
System.out.println("header = " + jwsObject.getHeader());
System.out.println("header = " + jwsObject.getHeader().getX509CertChain());
System.out.println("payload = " + jwsObject.getPayload().toJSONObject());
System.out.println("signature = " + jwsObject.getSignature());
System.out.println("signature = " + jwsObject.getSignature().decodeToString());
} catch (ParseException e) {
e.printStackTrace();
}
Some nice examples are provided:-
https://connect2id.com/products/nimbus-jose-jwt/examples

Error 200 (job canceled) when streaming data from Android to BigQuery

I've been trying to build some functionality into my app too allow user-generated data (EEG recordings) to be sent to a central BigQuery database.
I've never done any networking code in Java before, so I shied away from doing the POST or REST-based strategies recommended here. The BigQuery Java client library seemed to be exactly what I needed, though I was completely confused why it wouldn't officially support Android.
Still, I came across this example Android app (from Google no less) that promised to do exactly what I wanted with the BigQuery Client library. I incorporated it into my app as follows:
// .... in an AsyncTask
#Override
protected String doInBackground(String... params) {
String CSV_CONTENT = params[0];
try {
AssetManager am = MainApplication.getInstance().getAssets();
InputStream isCredentialsFile = am.open(CREDENTIALS_FILE);
BigQuery bigquery = BigQueryOptions.builder()
.authCredentials(AuthCredentials.createForJson(isCredentialsFile))
.projectId( PROJECT_ID )
.build().service();
TableId tableId = TableId.of(DATASET,TABLE);
Table table = bigquery.getTable(tableId);
int num = 0;
Log.d("Main", "Sending CSV: ");
WriteChannelConfiguration configuration = WriteChannelConfiguration.builder(tableId)
.formatOptions(FormatOptions.csv())
.build();
try (WriteChannel channel = bigquery.writer(configuration)) {
num = channel.write(ByteBuffer.wrap(CSV_CONTENT.getBytes(StandardCharsets.UTF_8)));
} catch (Exception e) {
Log.d("Main", e.toString());
}
Log.d("Main", "Loading " + Integer.toString(num) + " bytes into table " + tableId);
} catch (Exception e) {
Log.d("Main", "Exception: " + e.toString());
}
return "Done";
}
This runs without any errors and fires off an API call that is detected by Google Cloud Storage. However, it returns error 200 (job was cancelled) every time. I don't understand how this could be since I'm not doing anything in the code to cancel the request and I don't see how the async task I put the call in could be cancelled.
Was this just a bad example app I copied and a bad usage of the BigQuery Client? If so, what's the best way to send data to BigQuery from Android?

How to receive file on Spring server send from a client using retrofit2

I am currently trying to upload a file from an android client using retrofit2 to a server using Spring Boot and its REST api.
CLIENT
I specifiy the upload method as described here: https://github.com/square/retrofit/issues/1063
public interface RetroRespondService {
#Multipart
#POST("/v1/answers")
public Call<ResponseDTO> sendPictures(#Part("file\"; filename=\"image.png")RequestBody image);
}
In another class the method to provide the actual image is declared:
(Now its just a test scenario. When image uploading is actually accomplished it will get more sophisticated.)
public void performAnswerRequest() {
try {
if (mRetrofit == null) {
mRetrofit = new Retrofit.Builder()
.baseUrl(DataHolder.getHostName())
.build();
}
//load test image
AssetManager manager = getAssets();
File file = new File(getFilesDir(), "image.png");
Utility.writeBytesToFile(new BufferedInputStream(manager.open("heart.png")), file);
RetroRespondService requestService = mRetrofit.create(RetroRespondService.class);
RequestBody image= RequestBody.create(MediaType.parse("multipart/form-data"), file);
Call<ResponseDTO> response = requestService.sendPictures(image);
response.enqueue(new AsyncAnswerResponse());
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
SERVER
What I actually do not know is, how to properly get the image on the spring side.
#RequestMapping(value = API_VERSION + "/answers", method = RequestMethod.POST)
#ResponseBody
ResponseEntity<ResponseDTO> addAnswers(#RequestParam("file\"; filename=\"image.png") MultipartFile answers) throws DBEntryDoesNotExistException, EvaluationException, ParticipantException {
// In fact I have set a brake point here. Never entered the method yet, though
System.out.println("Yay!")
return null;
}
ERROR
Request: localhost:8080/v1/answers raised org.springframework.web.bind.MissingServletRequestParameterException:
Required MultipartFile parameter 'file"; filename="image.png' is not present
Since wireshark reports that in fact a request of size 1894 Bytes was send and this is the size of the image i want to upload I strongly believe the the data is actually transmitted but cannot be decoded from the server.
I have also seen this answers: How to config "CommonsMultipartResolver" in spring4 without xml to upload file
and subsequently implemented this class on the server side:
#Configuration
public class MultipartConfiguration {
#Bean
public CommonsMultipartResolver multipartResolver() {
CommonsMultipartResolver resolver=new CommonsMultipartResolver();
resolver.setDefaultEncoding("utf-8");
resolver.setMaxUploadSize(1048576);
return resolver;
}
}
If you have any pointers in how to solve this I would appreciate your answer tremendously :)
If there are any questions left unanswered feel free to ask away.
Btw.: Sending and receiving JSON encoded data works just fine in both directions.

Sony remote camera API QX 10, Error 1

I got "Error 1" without any comment as a response for most of my requests to Sony qx10 (last firmware 3.00).
For example:
03-10 13:22:50.830: D/SimpleRemoteApi(4418): Request: {"method":"getAvailableExposureCompensation","params":[],"id":11,"version":"1.0"}
03-10 13:22:51.012: D/SimpleRemoteApi(4418): Response: {"error":[1,""],"id":11}
Same result have
getAvailableWhiteBalance
getAvailableIsoSpeedRate
getAvailableExposureCompensation
But getAvailableStillSize returns proper response with list of image sizes.
Also getAvailableFocusMode returns error "40401, Camera Not Ready". What does it mean? Liveview is started, and camera is sending images to phone.
All my request are sent in this way (just a bit modified code from example SDK):
public JSONObject getSomeParameter() throws IOException {
String service = "camera";
try {
JSONObject requestJson =
new JSONObject().put("method", "getSomeParameter") //
.put("params", new JSONArray()).put("id", id()) //
.put("version", "1.0");
String url = findActionListUrl(service) + "/" + service;
log("Request: " + requestJson.toString());
String responseJson = SimpleHttpClient.httpPost(url, requestJson.toString());
log("Response: " + responseJson);
return new JSONObject(responseJson);
} catch (JSONException e) {
throw new IOException(e);
}
}
My questions are:
How to solve error 1?
How to solve error 40401?
Is there more detailed documentation for errors and other stuff, then PDF supplied with SDK usage example?
To get availability to control settings of camera (such as Exposure compensation, WB mode, ISO mode) you should call "setExposureMode" with parameter "Program Auto".

Create GoogleCredential by Access Token

I want to design an Android file viewer for Google Drive.
At first, I implemented the app by using of the Google Android API, as follows,
private void retrieveNextPage(){
if(mHasMore == false)
return;
Query query = new Query.Builder().setPageToken(mNextPageToken).build();
com.google.android.gms.drive.Drive.DriveApi.query(getGoogleApiClient(), query).setResultCallback(metadataBufferResultResultCallback);
}
However, the Android Drive API only allows the app to view and fetch the files that created by itself. I cannot access other files on the drive through the app.
Therefore, I turned to another option, directly manipulate the Java Drive API.
According to the example on developer guide for Java,
https://developers.google.com/drive/web/quickstart/quickstart-java
The users have to manually copy and paste the "Authorization Code" between the browser and app, which is not a practical way to acquire the Access Token in Android.
To come out a new way, I used the GoogleAuthUtil in Android API to acquire the Access Token, coincided with the GoogleCredential and Drive in Java API to fetch the file list, as follows,
private static List<File> retrieveFiles(Drive service) throws IOException{
List<File> result = new ArrayList<File>();
Files.List request = service.files().list();
do {
try{
FileList fileList = request.execute();
result.addAll(fileList.getItems());
request.setPageToken(fileList.getNextPageToken());
}catch (IOException e){
Log.d(dbgT + "JavaRetrieveFiles", "Retrieved Failed");
request.setPageToken(null);
}
}while (request.getPageToken() != null && request.getPageToken().length() > 0);
return result;
}
private class RetrieveTokenTask extends AsyncTask<String, Void, String>{
#Override
protected String doInBackground(String... params){
String accountName = params[0];
String scopes = "oauth2:" + "https://www.googleapis.com/auth/drive";
String token = null;
try{
token = GoogleAuthUtil.getToken(getApplicationContext(), accountName, scopes);
}
catch (IOException e){
Log.e(excpTAG, "IO Exception: " + e.getMessage());
}
catch (UserRecoverableAuthException e){
startActivityForResult(e.getIntent(), REQ_SIGN_IN_REQUIRED);
}
catch (GoogleAuthException e)
{
Log.e(excpTAG, "GoogleAuthException: " + e.getMessage());
}
return token;
}
#Override
protected void onPostExecute(String s){
super.onPostExecute(s);
//Get Access Token
Log.d( dbgT + "Token", s);
EditText tokenText = (EditText) findViewById(R.id.tokenText);
tokenText.setText(s);
EditText fileNameText = (EditText) findViewById(R.id.editTextMeta);
GoogleCredential credential = new GoogleCredential().setAccessToken(s);
HttpTransport httpTransport = new NetHttpTransport();
JsonFactory jsonFactory = new JacksonFactory();
Drive service = new Drive.Builder(httpTransport, jsonFactory, null).setHttpRequestInitializer(credential).build();
List<File> fileList;
try{
fileList = retrieveFiles(service);
for(int i=0; i< fileList.size(); i++)
fileNameText.append(fileList.get(i).getTitle());
}catch(IOException e){
Log.d(dbgT + "RetrieveFileList", "IO Exception" );
}
}
}
Unfortunately, the app always crashes by the causing of NetworkOnMainThreadException when request.execute() in retrieveFiles is invoked.
I checked my access token s, it is usually in form of ya29.xxx...etc., and it can also be passed to my other .NET program for retrieving files from Google Drive. Therefore I can certain the access token is correct.
So my question is, how to create a correct GoogleCredential by using of access token, instead of applying authorization code in setFromTokenResponse ?
Thanks in advance.
Many thanks for Andy's tips, this problem is simply caused by the network operations occurs on the main thread, which is a very basic newbie error.
The Drive in Google Drive SDK for Java, using network libraries without any background/thread worker, and now it is functional after I put the retrieveFiles() into background.
Applying the GoogleAuthUtil in Google Play Android SDK to acquire the access token, and followed by GoogleCredential+Drive in Java SDK that use the token to do the file operation in Google Drive.
This is a right way to avoid the scope restriction in Android SDK for Google Drive, allowing the developers to acquire the full permissive of accessing Google Drive.

Categories

Resources