Upload a file from Android to Drupal via Services Module - android

I tried to upload an image from Android Device to a Drupal Website
I used the Service module on Drupal side with method file.create.
The problem is that the files has been broken after the download.
The Files are smaller.
The filemime setting has n effect.
Javaside:
public Map saveFile(String localFilePath, String serverpath) throws XmlRpcException, IOException{
HashMap<String, Object> account = new HashMap<String, Object>();
File localFile = new File(localFilePath);
Log.w("XMLRPC", "FILENAME "+localFile.getName() + " original "+localFile.length());
String fileAsString = readFile(localFilePath);
// Sending side
byte[] data = fileAsString.getBytes("UTF-8");
byte[] base64 = Base64.encode(data,Base64.URL_SAFE);
account.put("file", base64);
account.put("filename", localFile.getName());
account.put("filepath", serverpath +"/"+ localFile.getName());
// account.put("filemime", "application/x-compressed");
account.put("filesize", Integer.parseInt(localFile.length()+""));//String.valueOf(localFile.length()));
Vector<Object> params = new Vector<Object>();
params.add(account);
return (Map) xmlRpcClient.execute(METHOD_FILE_CREATE, params);
}

Well, if you are not using a native android app for your Drupal site, you do not need any other 'interface' to upload the file! It should work as if you are uploading the file from desktop!

Related

How to specify Google Drive API spaces using CloudRail

I am writing an Android App to access Google Drive Photos via CloudRail service. I am able to authenticate to the Google account in question and see all my files/folders in the Google Drive, but I can't access photos from Google Photos.
While browsing through the Drive API documentation, it makes a reference to spaces, specifically 3 spaces are defined: drive, photos and allDataFolder.
Where do I specify the spaces that I am interested in? But default, the drive space is being accessed. Even though, I specifically specified scope for photos:
https://www.googleapis.com/auth/drive.photos.readonly
And when Google Authentication pages opens in the mobile browser, it states that my app wants to gain access to the user's Google Photos and I grant this access. But when calling CloudRail service to get children, no photos are visible
`googledriveChildren = mGoogledriveService.getChildren("/"); // returns goole drive top level files/folders
`googledriveChildren = mGoogledriveService.getChildren("/photos"); // generates a NotFoundException
I have already been down this path and achieved the integration - with the help/guidance from the folks at Cloudrail. You should note that my integration is limited to reading/downloading from Google Photos. I have not found any way to write/upload. Nor have I found any way of reading the album structure that can be set up in Google Photos.
First, you need to include the scope for Google Photos. I did this as follows:
public static final String GOOGLE_PHOTOS_SCOPE = "https://www.googleapis.com/auth/drive.photos.readonly";
private final AtomicReference<CloudStorage> googlephotos = new AtomicReference<>();
List<String> scope = new ArrayList<>();
scope.add(My_Constants.GOOGLE_PHOTOS_SCOPE);
googlephotos.set(new GoogleDrive(context, google_client_id, "", Get.GetString(R.string.google_redirect_uri),
Get.GetString(R.string.google_authentication_state), scope));
((GoogleDrive) googlephotos.get()).useAdvancedAuthentication();
You then need to build a Cloudrail advancedRequest to download whatever data you want. I download the metadata I require as follows:
CloudStrorage service = googlephotos.get();
private void searchForGooglePhotos(final CloudStorage service) throws Throwable {
GoogleDrive google_drive = (GoogleDrive) service;
boolean more = true;
String pageToken = null;
while (more) {
StringBuilder builder = new StringBuilder();
String query = URLEncoder.encode("mimeType='image/jpeg' and trashed = false", "utf-8");
builder.append("/files?spaces=photos");
if (pageToken != null) {
builder.append("&pageToken=");
builder.append(pageToken);
}
builder.append("&q=");
builder.append(query);
builder.append("&fields=nextPageToken,files(id,name,modifiedTime,description,size," +
"imageMediaMetadata(height,rotation,width,time))");
AdvancedRequestSpecification specification = new AdvancedRequestSpecification(builder.toString());
AdvancedRequestResponse response = google_drive.advancedRequest(specification);
#SuppressWarnings("unchecked")
Map<String, Object> resultObjectMap = (Map<String, Object>) response.getBodyJsonParsed();
pageToken = (String) resultObjectMap.get("nextPageToken");
#SuppressWarnings("unchecked")
ArrayList<Map<String, Object>> filesObjectMap = ((ArrayList<Map<String, Object>>) resultObjectMap.get("files"));
for (Map<String, Object> fileObjectMap : filesObjectMap) {
// process downloaded files
}
more = (pageToken != null);
}
}
Subsequently in my app I use Glide to download the photos themselves when required. In the Glide DataFetcher I obtain the inputStream using:
if (model.getSourceRecord().isTypeGooglePhotos()) {
AdvancedRequestSpecification specification;
AdvancedRequestResponse response;
if (model.getIsThumbnail()) {
specification = new AdvancedRequestSpecification("/files" + model.getSourceId() +
"?spaces=photos&fields=thumbnailLink");
response = ((GoogleDrive) service).advancedRequest(specification);
#SuppressWarnings("unchecked")
Map<String, Object> parsed = (Map<String, Object>) response.getBodyJsonParsed();
String link = (String) parsed.get("thumbnailLink");
specification = new AdvancedRequestSpecification(link);
specification.disableBaseUrl();
} else {
specification = new AdvancedRequestSpecification("/files" + model.getSourceId() + "?spaces=photos&alt=media");
}
response = ((GoogleDrive) service).advancedRequest(specification);
input_stream = response.getBodyAsStream();
} else {
if (model.getIsThumbnail()) {
input_stream = service.getThumbnail(model.getSourceId());
} else {
input_stream = service.download(model.getSourceId());
}
}
Here, "model" contains various info associated with each photo. The sourceId comes from the "id" downloaded:
String source_id = java.io.File.separator + fileObjectMap.get("id");
I hope this helps.
Would anyone arriving at this question / response please note that, as of mid Jan 2018, Google have "sunset" (sic) the photos space (spaces=photos above). This means that the above solution no longer works.
On the Google REST API documentation: "The photos space will sunset in early January 2018. Your users can continue to access Google Photos via the drive space by enabling the Google Photos folder in My Drive in the Drive client settings"
Ugh!

Dropbox API on Android - Using Token to download file from Apps folder

I generated an access token to be able to make API calls for my own account without going through the authorization flow. I found this Dropbox files Get API but I don't know how to use it.
I tried this code, but it doesn't seem to work:
// Authentication with Token
AppKeyPair appKeys = new AppKeyPair(APP_KEY, APP_SECRET);
AndroidAuthSession session = new AndroidAuthSession(appKeys);
mDBApi = new DropboxAPI<AndroidAuthSession>(session);
mDBApi.getSession().setOAuth2AccessToken(ACCESS_TOKEN);
// Upload a file to Apps folder
File file = new File("working-draft.txt");
FileInputStream inputStream = null;
try {
inputStream = new FileInputStream(file);
DropboxAPI.Entry response = mDBApi.putFile("/magnum-opus.txt", inputStream,
file.length(), null, null);
Log.i("DbExampleLog", "The uploaded file's rev is: " + response.rev);
} catch (Exception e) {
e.printStackTrace();
}
How can I upload and download directly to the Apps folder using the token key?
Also is there a way to print the list of all the files in my Apps folder?
The docs are pretty poor. I found the following examples on Github which helped me:
https://github.com/dropbox/dropbox-sdk-java/tree/master/examples/android/src/main/java/com/dropbox/core/examples/android
In gradle
compile 'com.dropbox.core:dropbox-core-sdk:3.0.2' or whatever is the latest
The key and secret are written into a JSON file + there's an entry you need to add into the manifest with the app key. Just follow the example which shows placeholders.
Once you've done the handshake and got the access token back
DbxRequestConfig requestConfig = DbxRequestConfig.newBuilder("your identifier")
.withHttpRequestor(new
OkHttp3Requestor(OkHttp3Requestor.defaultOkHttpClient()))
.build();
dbxClient = new DbxClientV2(requestConfig, accessToken);
dbxClient.files().[operation e.g. upload\download\file listing]

Android and AWS S3

i am creating an Android App within AWS mobile Hub, i would like to do know is it possible when a user Signs up to my Application that an S3-Bucket or even a Folder within an S3 bucket is created only for that user account ?
Yes, you can use PutObjectRequest(bucketName, keyName, file) to achieve your use case.
Create S3 folder
With AWS S3 Java SDK , just add "/" at the end of the key name, it will create empty folder.
var folderKey = key + "/"; //end the key name with "/"
Sample code:
final InputStream im = new InputStream() {
#Override
public int read() throws IOException {
return -1;
}
};
final ObjectMetadata om = new ObjectMetadata();
om.setContentLength(0L);
PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectName, im, om);
s3.putObject(putObjectRequest);

Serve mp3 stream for android with Laravel

Here's my problem: I'm writing a laravel backend which have to serve an mp3 file that had to be reproduced by using the android standard media player.
For the laravel backend I need to use JWT to handle authentication so on every request headers I have to set the "Authorization" field to "Bearer {token}" .The laravel route is "/songs/{id}" and is handled in this way:
public function getSong(Song $song) {
$file = new File(storage_path()."/songs/".$song->path.".mp3");
$headers = array();
$headers['Content-Type'] = 'audio/mpeg, audio/x-mpeg, audio/x-mpeg-3, audio/mpeg3';
$headers['Content-Length'] = $file->getSize();
$headers['Content-Transfer-Encoding'] = 'binary';
$headers['Accept-Range'] = 'bytes';
$headers['Cache-Control'] = 'must-revalidate, post-check=0, pre-check=0';
$headers['Connection'] = 'Keep-Alive';
$headers['Content-Disposition'] = 'attachment; filename="'.$song->path.'.mp3"';
$user = \Auth::user();
if($user->activated_at) {
return Response::download($file, $song->path, $headers);
}
\App::abort(400);
}
On the android side I'm using the MediaPlayer to stream the mp3 file in this way:
media_player = new MediaPlayer();
try {
media_player.setAudioStreamType(AudioManager.STREAM_MUSIC);
String token = getSharedPreferences("p_shared", MODE_PRIVATE).getString("token", null);
Map<String, String> headers = new HashMap<>();
headers.put("Authorization", "Bearer " + token);
media_player.setDataSource(
getApplicationContext(),
Uri.parse(ConnectionHelper.SERVER + "/songs/" + song.getId()),
headers
);
} catch (IOException e) {
finish();
Toast.makeText(
Round.this,
"Some error occurred. Retry in some minutes.",
Toast.LENGTH_SHORT
).show();
}
media_player.setOnCompletionListener(this);
media_player.setOnErrorListener(this);
media_player.setOnPreparedListener(this);
But every time I execute the code I get extra code -1005 on the error listener that means ERROR_CONNECTION_LOST.
The problem: Response::download(...) doesn't produce a stream, so I can't serve my .mp3 file.
The solution:
As Symfony HttpFoundation doc. says in the serving file paragraph:
"if you are serving a static file, you can use a BinaryFileResponse"
The .mp3 files I need to serve are statics in the server and stored in "/storage/songs/" so I decided to use the BinaryFileResponse, and the method for serving .mp3 became:
use Symfony\Component\HttpFoundation\BinaryFileResponse;
[...]
public function getSong(Song $song) {
$path = storage_path().DIRECTORY_SEPARATOR."songs".DIRECTORY_SEPARATOR.$song->path.".mp3");
$user = \Auth::user();
if($user->activated_at) {
$response = new BinaryFileResponse($path);
BinaryFileResponse::trustXSendfileTypeHeader();
return $response;
}
\App::abort(400);
}
The BinaryFileResponse automatically handle the requests and allow you to serve the file entirely (by making just one request with Http 200 code) or splitted for slower connection (more requests with Http 206 code and one final request with 200 code).
If you have the mod_xsendfile you can use (to make streaming faster) by adding:
BinaryFileResponse::trustXSendfileTypeHeader();
The android code doesn't need to change in order to stream the file.

Using Google Cloud Storage JSON api in android

I want to upload image on Google Cloud Storage from my android app. For that I searched and found that GCS JSON Api provides this feature. I did a lot of research for Android sample which demonstrates its use. On the developer site they have provided code example that only support java. I don't know how to use that API in Android. I referred this and this links but couldn't get much idea. Please guide me on how i can use this api with android app.
Ok guys so I solved it and got my images being uploaded in Cloud Storage all good.
This is how:
Note: I used the XML API it is pretty much the same.
First, you will need to download a lot of libraries.
The easiest way to do this is create a maven project and let it download all the dependencies required. From this sample project :
Sample Project
The libraries should be:
Second, you must be familiar with Cloud Storage using the api console
You must create a project, create a bucket, give the bucket permissions, etc.
You can find more details about that here
Third, once you have all those things ready it is time to start coding.
Lets say we want to upload an image:
Cloud storage works with OAuth, that means you must be an authenticated user to use the API. For that the best way is to authorize using Service Accounts. Dont worry about it, the only thing you need to do is in the API console get a service account like this:
We will use this service account on our code.
Fourth, lets write some code, lets say upload an image to cloud storage.
For this code to work you must put your key generated in step 3 in assets folder, i named it "key.p12".
I don't recommend you to do this on your production version, since you will be giving out your key.
try{
httpTransport= new com.google.api.client.http.javanet.NetHttpTransport();
//agarro la key y la convierto en un file
AssetManager am = context.getAssets();
InputStream inputStream = am.open("key.p12"); //you should not put the key in assets in prod version.
//convert key into class File. from inputstream to file. in an aux class.
File file = UserProfileImageUploadHelper.createFileFromInputStream(inputStream,context);
//Google Credentianls
GoogleCredential credential = new GoogleCredential.Builder().setTransport(httpTransport)
.setJsonFactory(JSON_FACTORY)
.setServiceAccountId(SERVICE_ACCOUNT_EMAIL)
.setServiceAccountScopes(Collections.singleton(STORAGE_SCOPE))
.setServiceAccountPrivateKeyFromP12File(file)
.build();
String URI = "https://storage.googleapis.com/" + BUCKET_NAME+"/"+imagename+".jpg";
HttpRequestFactory requestFactory = httpTransport.createRequestFactory(credential);
GenericUrl url = new GenericUrl(URI);
//byte array holds the data, in this case the image i want to upload in bytes.
HttpContent contentsend = new ByteArrayContent("image/jpeg", byteArray );
HttpRequest putRequest = requestFactory.buildPutRequest(url, contentsend);
com.google.api.client.http.HttpResponse response = putRequest.execute();
String content = response.parseAsString();
Log.d("debug", "response is:"+response.getStatusCode());
Log.d("debug", "response content is:"+content);} catch (Exception e) Log.d("debug", "Error in user profile image uploading", e);}
This will upload the image to your cloud bucket.
For more info on the api check this link Cloud XML API
Firstly, You should get the below information by registering your application in the GCP console.
private final String pkcsFile = "xxx.json";//private key file
private final String bucketName = "your_gcp_bucket_name";
private final String projectId = "your_gcp_project_id";
Once you get the credentials, you should put the private key (.p12 or .json) in your assets folder. I'm using JSON format private key file. Also, you should update the image location to upload.
#RequiresApi(api = Build.VERSION_CODES.O)
public void uploadImageFile(String srcFileName, String newName) {
Storage storage = getStorage();
File file = new File(srcFileName);//Your image loaction
byte[] fileContent;
try {
fileContent = Files.readAllBytes(file.toPath());
} catch (IOException e) {
e.printStackTrace();
return;
}
if (fileContent == null || fileContent.length == 0)
return;
BlobInfo.Builder newBuilder = Blob.newBuilder(BucketInfo.of(bucketName), newName);
BlobInfo blobInfo = newBuilder.setContentType("image/png").build();
Blob blob = storage.create(blobInfo, fileContent);
String bucket = blob.getBucket();
String contentType = blob.getContentType();
Log.e("TAG", "Upload File: " + contentType);
Log.e("File ", srcFileName + " uploaded to bucket " + bucket + " as " + newName);
}
private Storage getStorage() {
InputStream credentialsStream;
Credentials credentials;
try {
credentialsStream = mContext.getAssets().open(pkcsFile);
credentials = GoogleCredentials.fromStream(credentialsStream);
} catch (IOException e) {
e.printStackTrace();
return null;
}
return StorageOptions.newBuilder()
.setProjectId(projectId).setCredentials(credentials)
.build().getService();
}

Categories

Resources