Monday, July 10, 2017

Upload image to local server using retrofit

First, create a folder "uploads" in the desire directory. Our uploaded photos will be stored in this folder.

Let's finish server side coding first. Create a file uploadImage.php . Here is the content of the uploadImage.php file :
$folder = "uploads/";
$path = $_FILES['uploadimage']['name'];
// $extension = pathinfo($path, PATHINFO_EXTENSION);
$fileName = basename($path);

$response = array();
$code = "";
$message = "";

if ($_SERVER['REQUEST_METHOD'] == "POST") {
    
    if ($_FILES['uploadimage']) {
        
        $destination = $folder.$fileName;
        if (move_uploaded_file($_FILES['uploadimage']['tmp_name'], $destination)) { // key = uploadimage
            $code = 1;
            $message = "Image successfully uploaded!";
        } else {
            $code = 0;
            $message = "Error! Image is not uploaded!";
            
            print_r(error_get_last());
        }
        
    } else {
        $code = 0;
        $message = "Error while uploading!";
    }
    
} else {
    $code = 0;
    $message = "Error! HTTP method mismatch!";
}

$response['code'] = $code;
$response['message'] = $message;

echo json_encode($response);
Here a JSON response will be created where 'code' & 'message' are two attributes.

Now we'll do the android side coding. I am going to use android studio. Create a project. First, add the necessary permission in AndroidManifest.xml file. We need permission for accessing internet & to read external storage. Add the following lines :

<uses-permission android:name="android.permission.INTERNET"></uses-permission>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"></uses-permission>
Now we gonna add dependency. Add the followings in build.gradle file to include retrofit & cardview in our project.

    compile 'com.squareup.retrofit2:retrofit:2.3.0'
    compile 'com.squareup.retrofit2:converter-gson:2.3.0'
    compile 'com.android.support:cardview-v7:23.1.1'
Our layout will look like this :

Add the followings in activity.xml file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.example.android.MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Open Gallery"
            android:id="@+id/btnGallery"/>

        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Upload"
            android:id="@+id/btnUplopad"/>

    </LinearLayout>

    <android.support.v7.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_margin="10dp"
        android:elevation="5dp">

        <ImageView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scaleType="centerInside"
            android:id="@+id/img"
            android:visibility="gone"/>

    </android.support.v7.widget.CardView>

</LinearLayout>
So our layout is ready now.Initially the upload button is disabled (will disable it later in java) & the cardview's visibility is gone.

Now come to the next part. For "retrofit" purpose, we need 2 types of java class & an interface. One of the java classes will contain the JSON model. In other word, it contains fields just like the JSON response, i mean it is our model class. In the other one, we'll initialize the retrofit object. The interface will contain the api endpoints.

Now let's create the java model class. Create a class ImageModel.java . Remember, in JSON response, there are two attributes - "code" & "message". So our model class will contain two fields. Here is the full file :

public class ImageModel {

    @SerializedName("code")
    private String code;
    @SerializedName("message")
    private String message;

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}
Now create another class ApiClient.java . We gonna initialize retrofit here.

public class ApiClient {

    public static final String BASE_URL = "http://localhost/jnknotes/imageUploadToServer/";
    public static Retrofit retrofit = null;

    public static Retrofit getApiClient () {
        if (retrofit == null) {
            retrofit = new Retrofit.Builder().baseUrl(BASE_URL).addConverterFactory(GsonConverterFactory.create()).build();
        }
        return retrofit;
    }

}
 Here BASE_URL is the location of our project in our local server. We have to find out the IP address , then replace "localhost" with IP address in BASE_URL.
getApiClient() method will return the retrofit object after initialization. Thats all about ApiClient class.

Next create an interface ApiInterface.java. This will include our api endpoint.

public interface ApiInterface {
 
    @Multipart
    @POST("uploadImage.php")
    Call<imagemodel> uploadTheImage(@Part MultipartBody.Part image);
 
}
In the POST annotaion we have specified the endpoint which is uploadImage.php.

So, our model class, api client class & interface all are ready. No we can move to the activity.java class. Initially, when there is no image is selected, the upload button will be disabled. This is done in onCreate() method.

uploadButton.setEnabled(false);

Now we want to open the gallery of all images . This is done in the onClick method of the openGalleryButton. Earlier we initialized an integer value REQUEST_CODE which we used later in onActivityResult() method.

                Intent intent = new Intent();
                intent.setType("image/*");
                intent.setAction(Intent.ACTION_GET_CONTENT);
                startActivityForResult(intent, REQUEST_CODE);

Here is the overrided method onActivityResult :

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == REQUEST_CODE && resultCode == RESULT_OK && data != null) {
            Uri uri = data.getData();
            String[] imageProjection = {MediaStore.Images.Media.DATA};

            Cursor cursor = getContentResolver().query(uri, imageProjection, null, null, null);
            if (cursor != null) {
                cursor.moveToFirst();

                int imageIndex = cursor.getColumnIndex(imageProjection[0]);
                partImage = cursor.getString(imageIndex);

                if (partImage != null) {
                    File image = new File(partImage);
                    imageView.setImageBitmap(BitmapFactory.decodeFile(image.getAbsolutePath()));
                    imageView.setVisibility(View.VISIBLE);
                    openGalleryButton.setEnabled(false);
                    uploadButton.setEnabled(true);
                }
            }
        }
    }

Now if we select an image , it will be displayed. Notice, now openGalleryButton is disabled & the uploadButton is enabled. Now we have to handle the uploadButton button's onClick method. Here it is :

public void upPhoto () {
        File file = new File(partImage);
        RequestBody requestBody = RequestBody.create(MediaType.parse("multipart/form-file"), file);
        MultipartBody.Part prt_image = MultipartBody.Part.createFormData("uploadimage", file.getName(), requestBody);
 
        ApiInterface apiInterface = ApiClient.getApiClient().create(ApiInterface.class);
        Call<imagemodel> call = apiInterface.uploadTheImage(prt_image);
 
        call.enqueue(new Callback() {
            @Override
            public void onResponse(Call<imagemodel> call, Response response) {
                Log.d("Retro", "On Response : " + response.body().toString());
                if (response.body().getCode().equals("1")) {
                    Toast.makeText(MainActivity.this, "Response : " + response.body().getMessage(), Toast.LENGTH_LONG).show();
                } else {
                    Toast.makeText(MainActivity.this, "Response : " + response.body().getMessage(), Toast.LENGTH_LONG).show();
                }
                uploadButton.setEnabled(false);
                imageView.setVisibility(View.GONE);
                openGalleryButton.setEnabled(true);
            }
 
            @Override
            public void onFailure(Call<imagemodel> call, Throwable t) {
                Log.d("Retro", "On Error : " + t);
            }
        });
    }

Here partImage is a string which was initialized while selecting image from gallery. Later we created an ApiInterface object and called it's uploadTheImage() method. In the overrided method onResponse() we handled the JSON response by calling response.body(). Other things are pretty straight forward. Now notice this in upPhoto() method  :

MultipartBody.Part prt_image = MultipartBody.Part.createFormData("uploadimage", file.getName(), requestBody);

Here the 1st parameter "uploadimage" is exactly the same that we used in the php file which was :

 $_FILES['uploadimage']

Here is the full activity.java class :

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

    Button openGalleryButton, uploadButton;
    ImageView imageView;
    final int REQUEST_CODE = 455;
    String partImage;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        openGalleryButton = (Button) findViewById(R.id.btnGallery);
        uploadButton = (Button) findViewById(R.id.btnUplopad);
        imageView = (ImageView) findViewById(R.id.img);

        uploadButton.setEnabled(false);

        openGalleryButton.setOnClickListener(this);
        uploadButton.setOnClickListener(this);

    } // end of main()

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btnGallery :
                Intent intent = new Intent();
                intent.setType("image/*");
                intent.setAction(Intent.ACTION_GET_CONTENT);
                startActivityForResult(intent, REQUEST_CODE);
                break;

            case R.id.btnUplopad :
                upPhoto();
                break;
        }
    } // end of onClick()

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == REQUEST_CODE && resultCode == RESULT_OK && data != null) {
            Uri uri = data.getData();
            String[] imageProjection = {MediaStore.Images.Media.DATA};

            Cursor cursor = getContentResolver().query(uri, imageProjection, null, null, null);
            if (cursor != null) {
                cursor.moveToFirst();

                int imageIndex = cursor.getColumnIndex(imageProjection[0]);
                partImage = cursor.getString(imageIndex);

                if (partImage != null) {
                    File image = new File(partImage);
                    imageView.setImageBitmap(BitmapFactory.decodeFile(image.getAbsolutePath()));
                    imageView.setVisibility(View.VISIBLE);
                    openGalleryButton.setEnabled(false);
                    uploadButton.setEnabled(true);
                }
            }
        }
    } // end of onActivityResult()

    public void upPhoto () {
        File file = new File(partImage);
        RequestBody requestBody = RequestBody.create(MediaType.parse("multipart/form-file"), file);
        MultipartBody.Part prt_image = MultipartBody.Part.createFormData("uploadimage", file.getName(), requestBody);

        ApiInterface apiInterface = ApiClient.getApiClient().create(ApiInterface.class);
        Call call = apiInterface.uploadTheImage(prt_image);

        call.enqueue(new Callback() {
            @Override
            public void onResponse(Call call, Response response) {
                Log.d("Retro", "On Response : " + response.body().toString());
                if (response.body().getCode().equals("1")) {
                    Toast.makeText(MainActivity.this, "Response : " + response.body().getMessage(), Toast.LENGTH_LONG).show();
                } else {
                    Toast.makeText(MainActivity.this, "Response : " + response.body().getMessage(), Toast.LENGTH_LONG).show();
                }
                uploadButton.setEnabled(false);
                imageView.setVisibility(View.GONE);
                openGalleryButton.setEnabled(true);
            }

            @Override
            public void onFailure(Call call, Throwable t) {
                Log.d("Retro", "On Error : " + t);
            }
        });
    }

}

Now time to run & check the app.




0 comments:

Post a Comment