Android - How to download images from s3? - android

I'm reading the s3 android guide and im really confused on how to download my files.
They provide this code:
TransferObserver observer = transferUtility.download(
MY_BUCKET, /* The bucket to download from */
OBJECT_KEY, /* The key for the object to download */
MY_FILE /* The file to download the object to */
);
So what is MY_FILE? am i suppose to make a local empty file object and supply it into that transferUtility download function and it fills that empty file to the one download?
And, when i finish getting the file, (particularly for images) how do i upload that file into an imageView using glide or Picasso?
I am not sure how to use the TransferObserver object.
Hope someone can provide a working example, please!
cheers!

Although I am quite late answering this question. Hope this helps someone who is stuck in this problem.
You don't need to make the bucket public. You can directly show the image via Glide. Here is my repo to load image from amazon s3 bucket via Glide.
https://github.com/jontyankit/Glide-Amazon-Image-Load
You need to override GlideModule and register our component
public class CustomGlideModule implements GlideModule {
#Override
public void applyOptions(Context context, GlideBuilder builder) {
builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888);
}
#Override
public void registerComponents(Context context, Glide glide) {
glide.register(ImageModel.class, InputStream.class, new ImageLoader.Factory());
}
}
Make custom ModelLoader class. This class fetches the image on the basis of model described above instead of URL
public class ImageLoader implements ModelLoader<ImageModel, InputStream> {
private final ModelCache<ImageModel, ImageModel> mModelCache;
public ImageLoader(ModelCache<ImageModel, ImageModel> mModelCache) {
this.mModelCache = mModelCache;
}
#Override
public DataFetcher<InputStream> getResourceFetcher(ImageModel model, int width, int height) {
ImageModel imageModel = model;
if (mModelCache != null) {
imageModel = mModelCache.get(model, 0, 0);
if (imageModel == null) {
mModelCache.put(model, 0, 0, model);
imageModel = model;
}
}
return new ImageFetcher(imageModel);
}
public static class Factory implements ModelLoaderFactory<ImageModel, InputStream> {
private final ModelCache<ImageModel, ImageModel> mModelCache = new ModelCache<>(500);
#Override
public ModelLoader<ImageModel, InputStream> build(Context context, GenericLoaderFactory factories) {
return new ImageLoader(mModelCache);
}
#Override
public void teardown() {
}
}
}
And at last make custom class DataFetcher. public InputStream loadData(Priority priority) is the method which will download image from Amazon.
public class ImageFetcher implements DataFetcher<InputStream> {
private final ImageModel imageModel;
private InputStream mInputStream;
boolean downloadComplete = false;
int transferId = 0;
public ImageFetcher(ImageModel imageModel) {
this.imageModel = imageModel;
}
#Override
public InputStream loadData(Priority priority) throws Exception {
return fetchStream(imageModel);
}
private InputStream fetchStream(final ImageModel imageModel) {
TransferUtility transferUtility = AmazonClient.getClient().getTransferUtility();
TransferObserver bolomessages = transferUtility.download(imageModel.getBucketName(), imageModel.getId(), new File(imageModel.getLocalPath()));
transferId = bolomessages.getId();
bolomessages.setTransferListener(new TransferListener() {
#Override
public void onStateChanged(int id, TransferState state) {
Log.wtf("AWSS3", "onStateChanged = " + state);
if (state == TransferState.COMPLETED) {
File initialFile = new File(imageModel.getLocalPath());
try {
mInputStream = new FileInputStream(initialFile);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
downloadComplete = true;
}
}
#Override
public void onProgressChanged(int id, long bytesCurrent, long bytesTotal) {
}
#Override
public void onError(int id, Exception ex) {
// do something
Log.wtf("AWSS3", "onError");
ex.printStackTrace();
downloadComplete = true;
}
});
while (!downloadComplete){}
return mInputStream;
}
#Override
public void cleanup() {
if (mInputStream != null) {
try {
mInputStream.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
mInputStream = null;
}
}
}
#Override
public String getId() {
return imageModel.getId();
}
#Override
public void cancel() {
AmazonClient.getClient().getTransferUtility().cancel(transferId);
}
}

So I am able to use Glide or picasso to load the image using the url of the image in the s3 bucket. But you have to make the bucket public.
Here is how you upload the image:
Glide.with(getActivity().getBaseContext()).load("IMAGE URL FROM S3").centerCrop().into(cardImage);
And thanks to #KNeerajLal here is how you can make your bucket public.
Here:
making bucket public

Related

How can I wait for an object filled asynchronously in Android UI thread without blocking it?

I have a singleton to handle the registration and elimination of an entity Profilo ( a Profile).
This entity is set by passing an identifier and gathering information on the server in an async way.
My problem is that when I have to return my instance of profilo if it's not still loaded it will return null.
public class AccountHandler {
private static AccountHandler istanza = null;
Context context;
private boolean logged;
private Profilo profilo;
private AccountHandler(Context context) {
this.context = context;
//initialization
//setting logged properly
assignField(this.getName());
}
}
public static AccountHandler getAccountHandler(Context context) {
if (istanza == null) {
synchronized (AccountHandler.class) {
if (istanza == null) {
istanza = new AccountHandler(context);
}
}
}
return istanza;
}
public void setAccount(String nickname, String accessingCode) {
logged = true;
assignField(nickname);
}
//other methods
private void assignField(String nickname) {
ProfiloClient profiloClient = new ProfiloClient();
profiloClient.addParam(Profilo.FIELDS[0], nickname);
profiloClient.get(new JsonHttpResponseHandler() {
#Override
public void onSuccess(int statusCode,
Header[] headers,
JSONArray response) {
JSONObject objson = null;
try {
objson = (JSONObject) response.getJSONObject(0);
} catch (JSONException e) {
e.printStackTrace();
}
AccountHandler accountHandler = AccountHandler.getAccountHandler(context);
// Profilo is created with a JSONObject
// **setProfilo is called in async**
**accountHandler.setProfilo(new Profilo(objson));**
}
});
}
private void setProfilo(Profilo profilo) {
this.profilo = profilo;
}
public Profilo getProfilo() {
if( logged && profilo == null)
//How can I wait that profilo is loaded by the JsonHttpResponseHandler before to return it
return this.profilo;
}
}
Instead of calling getProfilo you could use a callback mechanism in which the AccountHandler class notifies the caller when the profile has been loaded. e.g.
public void setAccount(String nickname, String accessingCode, MyCallback cb) {
assignField(nickname, cb);
}
private void assignField(String nickname, MyCallback cb) {
....
accountHandler.setProfilo(new Profilo(objson));
cb.onSuccess(this.profilo);
}
Create an inner Interface MyCallback (rename it) in your AccountHandler class
public class AccountHandler {
public interface MyCallback {
void onSuccess(Profilo profile);
}
}
Now whenever you call setAccount you will pass the callback and get notified when the profile is available e.g.
accountHandler.setAccount("Test", "Test", new AccountHandler.MyCallback() {
void onSuccess(Profilio profile) {
// do something with the profile
}
}
I added, as #Murat K. suggested, an interface to my Class that will provide a method to be call with the object when it is ready to be used.
public class AccountHandler {
public interface Callback {
void profiloReady(Profilo profilo);
}
}
This method is called in getProfilo in a Handler that makes recursive calls to getProfilo until profilo is ready to be used, then it call the callback method which class is passed as argument of getProfilo.
public void getProfilo(final Callback Callback) {
if( logged && (profilo == null || !profilo.isReady() ) {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
getProfilo(Callback);
}
}, 500);
}else
Callback.profiloReady(profilo);
}
Example of getProfilo call
public class ProfiloCall implements AccountHandler.MyCallback {
#Override
public void profiloReady(Profilo profilo) {
//Use profilo as needed
//EXECUTED ONLY WHEN PROFILO IS READY
}
public void callerMethod() {
//useful code
accountHandler.getProfilo(this);
//other useful code
}
}

Glide custom URI scheme

I need to load images from my database, I store them in blobs just like android. Each image is represented by my costume URI. How can I work it out with Glide?
I want to benefit from Glide cache and fast loading.
Is there a proper way of doing this?
you can register customize ModelLoader class to Glide by calling Glide.get(context).register() method. and in your ModelLoader, you can tell Glide how to load image resources from your database by implement getResourceFetcher method and return a customize DataFetcher instance.
here's a example:
DBImageUri class:
public class DBImageUri {
private String uriString;
public DBImageUri(String uriString){
this.uriString = uriString;
}
#Override
public String toString(){
return uriString;
}
}
DBDataFetcher class:
public class DBDataFetcher implements DataFetcher<InputStream> {
private DBImageUri uri;
private int width;
private int height;
private InputStream stream;
public DBDataFetcher(DBImageUri uri, int width, int height){
this.uri = uri;
this.width = width;
this.height = height;
}
#Override
public InputStream loadData(Priority priority){
String uriString = this.uri.toString();
stream = //**load image based on uri, and return InputStream for this image. this is where you do the actual image from database loading process**;
return stream;
}
#Override
public String getId(){
//width & height should be ignored if you return same image resources for any resolution (return uri.toString();)
return uri.toString() + "_" + width + "_" + height;
}
#Override
public void cleanup(){
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
// Ignore
}
}
}
#Override
public void cancel(){
}
}
DBModelLoader class:
public class DBModelLoader implements ModelLoader<DBImageUri, InputStream> {
#Override
public DataFetcher<InputStream> getResourceFetcher(DBImageUri model, int width, int height){
return new DBDataFetcher(model, width, height);
}
public static class Factory implements ModelLoaderFactory<DBImageUri, InputStream>{
#Override
public ModelLoader<DBImageUri, InputStream> build(Context context, GenericLoaderFactory factories){
return new DBModelLoader();
}
#Override
public void teardown(){
}
}
}
and then you add ModelLoader to Glide registry by calling:
Glide.get(context).register(DBImageUri.class, InputStream.class, new DBModelLoader.Factory());
now you can load you database images:
Glide.with(context).load(new DBImageUri(/*your unique id string for database image*/)).into(imageview);

How to pass parameters in Robospice result listener?

I have one request type and several request parameters. For example, i need to download some internet pages and i have param = "pageNumber". I called:
mRequest = new SpiceRaspRequest(pageNumber);
mSpiceManager.execute(mRequest, new RequestResultListener());
But in onRequestSuccess/onRequestFailure i have only "result". If i call several requests, I won't know - for what request they are, because results can fire asynchronously.
Is it possible to know - which result for what request is?
If you want to know the pageNumber you are getting a result for, you need to set the pageNumber in the result object after you downloaded it.
To do that, you will add a few lines in your SpiceRaspRequest and the result object ( let's call the result oject RaspPage)
public class RaspPage {
....
private int mPageNumber;
public getPageNumber() {
return mPageNumber;
}
public setPageNumber(int pageNumber) {
mPageNumber = pageNumber;
}
public class SpiceRaspRequest extends SpringAndroidSpiceRequest<RaspPage>{
int mPageNumber;
public SpiceRaspRequest(int pageNumber) {
mPageNumber = pageNumber;
RestTemplate restTemplate= getRestTemplate();
// add these lines so that the Robospice does not look for the field //pageNumber that you have added to the RestPage class.
MappingJacksonHttpMessageConverter converter = new MappingJacksonHttpMessageConverter();
converter.setSupportedMediaTypes(Collections.singletonList(MediaType.APPLICATION_JSON));
converter.getObjectMapper().disable(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS);
restTemplate.getMessageConverters().add(converter);
}
#Overrides
public RaspPage loadDataFromNetwork throws Exception {
...
try {
// before returning the result you need to set the pageNumber
RaspPage result = restTemplate.exchange(uri, HttpMethod.GET, requestEntity, RaspPage.class);
result.setPageNumber(mPageNumber)
return result;
}catch (HttpClientErrorException e) {
message = e.getResponseBodyAsString();
throw new SpiceException(message);
}catch (HttpServerErrorException e) {
message = e.getResponseBodyAsString();
throw new SpiceException(message);
}
}
}
Now let's say your activity listens to the results, you will get the results through a callback called onRequestSuccess
MainActivity extends Activity implements RequestListener<RaspPage> {
private SpiceRaspRequest;
#Overrides
public void onCreate(Bundle onSavedInstanceState) {
super.onCreate();
...
mRequest = new SpiceRaspRequest(pageNumber);
mSpiceManager.execute(mRequest, this);
}
#Overrides
public void onRequestSuccess(RaspPage result) {
int pageNumber = result.getPageNumber();
// This is the page number you are looking for
}
#Overrides
public void onRequestFailure(SpiceException spiceException) {
// TODO : do something if you failed to download the page
}
}
If you have problems when a page failed to download, here's what you should do :
When a request fails, it fires a RestClientException, that exception will be in the parameter of the callback method onRequestFailed(SpiceException e)
In order to know which pageNumber failed to download, you can extend a SpiceException
public class PageFailedException extends RestClientException {
public int mPageNumber;
public PageFailedException(String detailMessage) {
super(detailMessage);
}
public int getPageNumber() {
return mPageNumber;
}
public void setPageNumber(int mPageNumber) {
this.mPageNumber = mPageNumber;
}
}
Then in your loadDataFromNetwork() method you should catch the SpiceException and set the corresponding pageNumber:
try {
ResponseEntity<RaspPage> result = restTemplate.exchange(uri, HttpMethod.GET, requestEntity, RaspPage.class);
RaspPage raspPage = result.getBody();
raspPage.setPageNumber(mPageNumber);
return raspPage;
}catch (PageFailedException e) {
e.setPageNumber(mPageNumber);
throw new SpiceException(e);
}
}
And finally, in your activity/fragment you will get the pageNumber that you have failed to download:
onRequestFailed(SpiceException e) {
if (e.getCause() instanceOf PageFailedException ) {
PageFailedException pageFailedException = (PageFailedException) e.getCause();
int pageFailedNumber = pageFailedException.getPageNumber();
}
}
Well, i find more simple and clear way. Just create individual listener for every request:
mRequest = new SpiceRaspRequest(pageNumber);
mSpiceManager.execute(mRequest, new RequestResultListener(pageNumber));
then in listeners:
private class RequestResultListener implements RequestListener<RaspRequest> {
private int pageNumber;
public RequestResultListener(int pageNumber) {
this.pageNumber = pageNumber;
}
#Override
public void onRequestFailure(SpiceException e) {
}
#Override
public void onRequestSuccess(RaspRequest raspRequest) {
}
}
Thats all! No need to override tons of code.

Volley + Picasso: Is it possible to use Volley as downloader for Picasso and its image cache structure

Hello StackOverflowers,
I developed project which is using Volley for communicating with REST web API. I use complex image loading process.
I want to load user's facebook profile picture and sometimes I want it first to get storage cached image and sometimes I want it to be always from internet.
The problem is that image url from facebook is always the same. This is why I made a basic check if image file size differs from the cached one.
I was using Volley's ImageLoader and it's cache implementation. Then I found it complex to do it with ImageLoader so I choose to use Picasso but it doesn't show cached image.
I know that for Picasso cache to work I need a custom "Downloader" and I know I can do it with OkHttp but my project is already using Volley for all REST calls.
So is it possible to use Volley as downloader for Picasso and its image cache structure?
Here is how I managed to fix the first issue and only the caching flow is the problem now:
public class PerfectImageLoader {
private static final String TAG = PerfectImageLoader.class.getSimpleName();
private Context mContext;
private SharedPreferences mSharedPreferences;
public PerfectImageLoader(Context context)
throws NullPointerException {
if (context == null) {
throw new NullPointerException("Context cannot be null");
}
mContext = context;
mSharedPreferences = context.getSharedPreferences(
context.getPackageName(), Context.MODE_PRIVATE);
}
public void getImage(final String imageUrl, final ImageView imageView) {
getImage(imageUrl, imageView, false);
}
public void getImage(final String imageUrl, final ImageView imageView,
final boolean isAwlaysFromInternet)
throws NullPointerException {
if (imageView == null) {
throw new NullPointerException("OnImageFromCacheListener cannot be null");
}
if (TextUtils.isEmpty(imageUrl)) {
throw new NullPointerException("image url cannot be null");
}
if (!isAwlaysFromInternet) {
loadCachedImage(imageUrl, imageView, null);
}
calculateFileSize(imageUrl, new OnFileSizeCheck() {
#Override
public void ready(final int networkFileSize) {
int cachedImageSize = mSharedPreferences
.getInt(imageUrl, 0);
TLog.v(TAG, "networkFileSize:" + networkFileSize);
TLog.v(TAG, "cachedImageSize:" + cachedImageSize);
if (cachedImageSize != networkFileSize || cachedImageSize == 0) {
TLog.v(TAG, "cachedImageSize != networkFileSize");
final Callback callback = new Callback() {
#Override
public void onSuccess() {
TLog.v(TAG, "downloaded");
mSharedPreferences.edit()
.putInt(imageUrl, networkFileSize).apply();
}
#Override
public void onError() {
TLog.v(TAG, "error");
if (isAwlaysFromInternet) {
mSharedPreferences.edit()
.remove(imageUrl).apply();
imageView.setImageBitmap(null);
}
}
};
if (isAwlaysFromInternet) {
TLog.v(TAG, "MemoryPolicy.NO_CACHE");
Picasso.with(mContext).load(imageUrl).memoryPolicy(MemoryPolicy.NO_CACHE)
.into(imageView, callback);
} else {
Picasso.with(mContext).load(imageUrl).into(imageView, callback);
}
} else {
TLog.v(TAG, "cachedImageSize == networkFileSize");
loadCachedImage(imageUrl, imageView, new Callback() {
#Override
public void onSuccess() {
}
#Override
public void onError() {
Picasso.with(mContext).load(imageUrl).into(imageView);
}
});
}
}
});
}
private void loadCachedImage(final String imageUrl, final ImageView imageView,
Callback callback) {
if (callback != null) {
Picasso.with(mContext)
.load(imageUrl)
.networkPolicy(NetworkPolicy.OFFLINE)
.into(imageView, callback);
} else {
Picasso.with(mContext)
.load(imageUrl)
.networkPolicy(NetworkPolicy.OFFLINE)
.into(imageView);
}
}
public static void calculateFileSize(String url, final OnFileSizeCheck fileSizeCallbacks)
throws NullPointerException {
if (fileSizeCallbacks != null && !TextUtils.isEmpty(url)) {
new AsyncTask<String, Void, Integer>() {
#Override
protected Integer doInBackground(String... params) {
Integer fileSize = null;
try {
URL urlObj = new URL(params[0]);
URLConnection urlConnection = urlObj.openConnection();
urlConnection.connect();
fileSize = urlConnection.getContentLength();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return fileSize;
}
#Override
protected void onPostExecute(Integer fileSize) {
super.onPostExecute(fileSize);
if (fileSizeCallbacks != null) {
fileSizeCallbacks.ready(fileSize);
}
}
}.execute(url);
} else {
if (fileSizeCallbacks == null) {
throw new NullPointerException("file size callbacks parameter is null");
}
if (url == null) {
throw new NullPointerException("url parameter is null");
} else if (TextUtils.isEmpty(url)) {
throw new NullPointerException("url parameter is empty");
}
}
}
}

asynchronus image loading in gridview

I'm loading images in gridviev asynchronusly.But my gridview displaying only a single image in the last cell of gridview.My adapter class and asynchronus class is given below, thanks.
Adapter class:
class OrderAdapter extends ArrayAdapter<String>
{
LayoutInflater inflater;
String name3[];
public OrderAdapter(Context context,int resource,LayoutInflater inflater,String name2[])
{
super(context, resource,R.id.img,name2);
this.inflater=inflater;
this.name3=name2;
}
public View getView(int position, View convertView, ViewGroup parent)
{
View row=inflater.inflate(R.layout.row,parent,false);
final ImageView img=(ImageView)row.findViewById(R.id.img);
String imgurl=name3[position];
Log.e("urlchandan",name3[position]);
AsyncImageLoaderv asyncImageLoaderv=new AsyncImageLoaderv();
Bitmap cachedImage = asyncImageLoaderv.loadDrawable(imgurl, new AsyncImageLoaderv.ImageCallback()
{
public void imageLoaded(Bitmap imageDrawable, String imageUrl) {
img.setImageBitmap(imageDrawable);
}
});
img.setImageBitmap(cachedImage);
return row;
}
}
Asynchronous class
public class AsyncImageLoaderv {
private HashMap<String, SoftReference<Bitmap>> imageCache;
public AsyncImageLoaderv() {
imageCache = new HashMap<String, SoftReference<Bitmap>>();
}
public Bitmap loadDrawable(final String imageUrl, final ImageCallback imageCallback) {
if (imageCache.containsKey(imageUrl)) {
SoftReference<Bitmap> softReference = imageCache.get(imageUrl);
Bitmap drawable = softReference.get();
if (drawable != null) {
return drawable;
}
}
final Handler handler = new Handler() {
#Override
public void handleMessage(Message message) {
imageCallback.imageLoaded((Bitmap) message.obj, imageUrl);
}
};
new Thread() {
#Override
public void run() {
try{
Log.d("ur",imageUrl);
Bitmap drawable = loadImageFromUrl(imageUrl);
imageCache.put(imageUrl, new SoftReference<Bitmap>(drawable));
Message message = handler.obtainMessage(0, drawable);
handler.sendMessage(message);
}catch(Exception e){Log.e("thread stellent",e.toString());}
}
}.start();
return null;
}
public static Bitmap loadImageFromUrl(String url) {
InputStream inputStream;Bitmap b;
try {
inputStream = (InputStream) new URL(url).getContent();
BitmapFactory.Options bpo= new BitmapFactory.Options();
bpo.inSampleSize=2;
b=BitmapFactory.decodeStream(new PatchInputStream(inputStream), null,bpo );
return b;
} catch (IOException e) {
throw new RuntimeException(e);
}
//return null;
}
public interface ImageCallback {
public void imageLoaded(Bitmap imageBitmap, String imageUrl);
}
}
You can't do it the way you're trying. You need to have your asynchronous loader store the resulting image in some data structure your adapter can access by position (e.g. a list, a hashmap, whatever). Your getView() should then simply pull the image from the correct position. Your asynchronous loader will populate the data structure and perform a notifyDataSetChanged() to have the list redraw itself with the newly loaded image.
I got the solution by making the ImageView img in adatper inflater as final because it avoids
the images to display at a single cell in gridview . And my images was of big size and got the error decoder return false and this error is solved by taking another class
--
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
public class PatchInputStream extends FilterInputStream {
public PatchInputStream(InputStream in) {
super(in);
}
public long skip(long n) throws IOException {
long m = 0L;
while (m < n) {
long _m = in.skip(n-m);
if (_m == 0L) break;
m += _m;
}
return m;
}
}
this class is used in AsyncImageLoaderv given above .
b=BitmapFactory.decodeStream(new PatchInputStream(inputStream), null,bpo );

Categories

Resources