Upload android gallery image to PHP, MySQL – Java

In this article, we will show you how you can upload image from your android gallery to PHP server.

Introduction

We will create a profile page in an android application where we wil display the user profile picture, name and a submit button. You can also display other fields as per your project. And when the form is submitted, we will send an HTTP request from android to PHP server that will save the user’s selected picture in PHP server and save its path in MySQL database along with its ID.

We will also discuss how you can display an image from a live server in an android application. We will be displaying the user profile image in circular form as in the screenshot.

In your app/build.gradle, make sure to add Volley, Glide and CircleImageView dependency:

implementation 'com.android.volley:volley:1.1.1'

// Used to display image from live HTTP server in imageview
implementation 'com.github.bumptech.glide:glide:3.7.0'

// To display image in circular form
implementation 'de.hdodenhof:circleimageview:2.2.0'

MySQL table structure

--
-- Table structure for table `users`
--

CREATE TABLE `users` (
  `id` int(11) NOT NULL,
  `name` text NOT NULL,
  `picture` text NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

--
-- Indexes for table `users`
--
ALTER TABLE `users`
  ADD PRIMARY KEY (`id`);

--
-- AUTO_INCREMENT for table `users`
--
ALTER TABLE `users`
  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;

We will have a “picture” name column in your user’s table. ID will be the primary unique key used to identify the user.

Now come to the android part. First, we will be creating a layout for the profile page. To keep the things simple, we are using name, and picture in the profile page:

layout/activity_profile.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/bg_login">

    <de.hdodenhof.circleimageview.CircleImageView
        android:id="@+id/image"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_marginTop="20dp"
        android:layout_marginBottom="10dp" />

    <TextView
        android:id="@+id/username"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/image"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="10dp"
        android:text="Username"
        android:textColor="#ffffff"
        android:textSize="22sp"
        android:textStyle="bold" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/username"
        android:orientation="vertical"
        android:padding="36dp">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:text="Full Name"
            android:textColor="#ffffff" />

        <EditText
            android:id="@+id/name"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:backgroundTint="#ffffff"
            android:inputType="text|textCapWords"
            android:maxLines="1"
            android:textColor="#ffffff"
            android:textColorHint="#ffffff" />

        <Button
            android:id="@+id/btn_update"
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:layout_gravity="center_horizontal"
            android:layout_marginTop="20dp"
            android:text="Update"
            android:textSize="12sp" />
    </LinearLayout>
</RelativeLayout>

In android, dp – Density-independent Pixels – is an abstract unit that is based on the physical density of the screen. These units are relative to a 160 dpi screen, so one dp is one pixel on a 160 dpi screen. The ratio of dp-to-pixel will change with the screen density, but not necessarily in direct proportion.

  1. We have a circular image view where user’s profile picture will be displayed. User can click on picture to edit.
  2. Then name of user below profile picture.
  3. A <LinearLayout> to display input fields and button vertically aligned.
  4. <EditText> to change name. You can add more fields, that depends on your project.
  5. And a <Button> which when clicked will submit the form and send an HTTP request to the PHP server to save the picture in folder and its path and in MySQL database. Also to store the name in MySQL.

Now in your profile activity’s onCreate method paste the following code. We will explain that line-by-line:

ProfileActivity.java

private final int PICK_IMAGE_REQUEST = 71;

private EditText name;
private ImageView image;
private Button btnUpdate;

private String selectedPicture = "";

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

    name = findViewById(R.id.name);

    image = findViewById(R.id.image);
    image.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Intent intent = new Intent();
            intent.setType("image/*");
            intent.setAction(Intent.ACTION_GET_CONTENT);
            startActivityForResult(Intent.createChooser(intent, "Select Picture"), PICK_IMAGE_REQUEST);
        }
    });
}

When image is clicked we will send an intent telling the android to open the gallery for images along with request ID in a variable PICK_IMAGE_REQUEST.

On image selected from gallery – Android

You will be asked to select an image from gallery. When the image is selected and the app is opened again, an event will be received in onActivityResult() function, so we will override it. It will contain the image data:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == PICK_IMAGE_REQUEST && resultCode == RESULT_OK
            && data != null && data.getData() != null) {
        Uri filePath = data.getData();
        try {
            Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), filePath);
            
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
            byte[] imageBytes = baos.toByteArray();
            selectedPicture = Base64.encodeToString(imageBytes, Base64.DEFAULT);

            byte[] bytesImage = Base64.decode(selectedPicture, Base64.DEFAULT);
            Glide.with(getApplicationContext())
                    .load(bytesImage)
                    .asBitmap()
                    .into(image);
        } catch (IOException e) {
            Toast.makeText(getApplicationContext(), "Error: " + e.getMessage(), Toast.LENGTH_LONG).show();
            e.printStackTrace();
        }
    }
}

We are converting user selected image into Bitmap and converting that into base64 since we can only upload the android gallery image on server via base64. Saving the base64 string in variable named selectedPicture. And then displaying that in image view using Glide.

You can compress the image if you want. And you can decide to set the format to PNG or JPEG.

Upload image to PHP server

Now when update button is clicked, we will send an HTTP request containing base64 string of picture, along with name, and ID of user. You might be getting the user ID from shared preference. Learn how to save value in shared preference from here. Paste the following code in your activity’s onCreate() method.

btnUpdate = findViewById(R.id.btn_update);
btnUpdate.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        String url = "http://localhost/tutorials/Http.php";
        // Instantiate the RequestQueue.
        RequestQueue queue = Volley.newRequestQueue(getApplicationContext());

        // Request a string response from the provided URL.
        StringRequest stringRequest = new StringRequest(Request.Method.POST, url,
                new Response.Listener<String>() {
                    @Override
                    public void onResponse(String response) {
                        Toast.makeText(getApplicationContext(), "Profile has been updated", Toast.LENGTH_LONG).show();
                    }
                }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                error.printStackTrace();
            }
        }) {
            @Override
            protected Map<String, String> getParams() {
                Map<String, String> params = new HashMap<>();
                params.put("id", "9");
                params.put("name", name.getText().toString());
                params.put("picture", selectedPicture);
                params.put("update_profile", "1");

                return params;
            }

            @Override
            public Map<String, String> getHeaders() throws AuthFailureError {
                Map<String, String> params = new HashMap<>();
                params.put("Content-Type", "application/x-www-form-urlencoded");
                return params;
            }
        };

        // To prevent timeout error
        stringRequest.setRetryPolicy(new DefaultRetryPolicy(50000, DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));

        // Add the request to the RequestQueue.
        stringRequest.setShouldCache(false);
        queue.add(stringRequest);
    }
});
  1. We are sending a request using Volley to Http.php, this will be the file which will save the image file and save its path in MySQL database.
  2. Request will be POST and it must have parameters of username, picture base64 picture and user ID. You can get user ID from shared preference. Learn how to save value in shared preference from here.
  3. When the response is received from server, then we are simply displaying a success message in Toast.
  4. We are setting retry policy to 50 seconds, which means that the request will wait for 50 seconds till the response is received from server.
  5. And we have disable the cache so it will always return fresh response from server.

Now we will move to the server side. In your Http.php file, paste the following code and we will explain that line-by-line:

<?php

ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);

$conn = mysqli_connect("localhost", "root", "root", "tutorials");

if (isset($_POST["update_profile"]))
{
    $id = $_POST["id"];
    $name = $_POST["name"];
    $picture = $_POST["picture"];

    $file_path = "profile/" . $id . ".jpg";

    file_put_contents($file_path, base64_decode($picture));

    $sql = "UPDATE `users` SET `name`='" . $name . "',`picture`='" . $file_path . "' WHERE id='" . $id . "'";
    mysqli_query($conn, $sql);

    echo "Profile has been updated";
    exit();
}

?>
  1. First we are enabling all error reporting for PHP.
  2. Then we are connecting with database.
  3. Then check if the request is for update profile. We are sending this value from Volley from android.
  4. Then get the name and base64 string of picture.
  5. Create a folder named “profile” in your project’s root directory.
  6. Then decode the base64 string and file_put_contents will save the file in “profile” folder.
  7. Finally update the MySQL database and send the response back to android app.

Display image from live server

Now that you have successfully uploaded an image from your android app to PHP server and saved its path in MySQL database. Now is the time to display that when user open the profile activity. Paste the following code in the onCreate() method when you successfully get the user records from database:

Glide.with(this)
    .load("http://localhost/tutorials/profile/9.jpg")
    .asBitmap()
    .diskCacheStrategy(DiskCacheStrategy.NONE)
    .skipMemoryCache(true)
    .into(image);

Where in load() function you will place the user actual image file store in your PHP server.

By default, Glide using caching to load images faster which are already loaded. But during development, we always want to fetch fresh data. So you can disable caching by setting DiskCacheStrategy to none.

So, now you can successfully upload image from your android gallery and save it in PHP server.

If you want to know how to fetch data from PHP and MySQL in your android app, you can follow this.