Uploading images to our server is a very frequently used thing. In most of the apps, we need user avatar, i.e. user profile image. So here is Android Upload Image to Server Tutorial.
In this post, we will see how we can upload images from our Android app to our server. Then we will also see how do we fetch the uploaded images back to our android application. So let’s begin.
Table of Contents
Tools Required
- Xampp/Wamp as for the server end I am going to use PHP and MySQL.
- Android Studio, it is obvious that we need it for building our application
Creating Database and RESTful API
Database
Here, we will upload and store the image file to our server’s directory, and in the database, we will save the path to the image with some tags for that picture. So the first thing here we need is the database.
- Go to localhost/phpmyadmin and create the following table names images in your database.
- To create the above-given table, you can use the following SQL Query.
1 2 3 4 5 6 7 |
CREATE TABLE `images` ( `id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT, `image` varchar(500) NOT NULL, `tags` varchar(500) NOT NULL ) |
- Once you have the database table, you can start building the API.
API
- Now create a folder inside c:/xampp/htdocs (the root directory if you are using xampp), you can name this folder anything. I have created a folder named MyApi.
- Inside the folder create a file named api.php and write the following php code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
<?php //Constants for database connection define('DB_HOST','localhost'); define('DB_USER','root'); define('DB_PASS','password'); define('DB_NAME','simplifiedcoding'); //We will upload files to this folder //So one thing don't forget, also create a folder named uploads inside your project folder i.e. MyApi folder define('UPLOAD_PATH', 'uploads/'); //connecting to database $conn = new mysqli(DB_HOST,DB_USER,DB_PASS,DB_NAME) or die('Unable to connect'); //An array to display the response $response = array(); //if the call is an api call if(isset($_GET['apicall'])){ //switching the api call switch($_GET['apicall']){ //if it is an upload call we will upload the image case 'uploadpic': //first confirming that we have the image and tags in the request parameter if(isset($_FILES['pic']['name']) && isset($_POST['tags'])){ //uploading file and storing it to database as well try{ move_uploaded_file($_FILES['pic']['tmp_name'], UPLOAD_PATH . $_FILES['pic']['name']); $stmt = $conn->prepare("INSERT INTO images (image, tags) VALUES (?,?)"); $stmt->bind_param("ss", $_FILES['pic']['name'],$_POST['tags']); if($stmt->execute()){ $response['error'] = false; $response['message'] = 'File uploaded successfully'; }else{ throw new Exception("Could not upload file"); } }catch(Exception $e){ $response['error'] = true; $response['message'] = 'Could not upload file'; } }else{ $response['error'] = true; $response['message'] = "Required params not available"; } break; //in this call we will fetch all the images case 'getpics': //getting server ip for building image url $server_ip = gethostbyname(gethostname()); //query to get images from database $stmt = $conn->prepare("SELECT id, image, tags FROM images"); $stmt->execute(); $stmt->bind_result($id, $image, $tags); $images = array(); //fetching all the images from database //and pushing it to array while($stmt->fetch()){ $temp = array(); $temp['id'] = $id; $temp['image'] = 'http://' . $server_ip . '/MyApi/'. UPLOAD_PATH . $image; $temp['tags'] = $tags; array_push($images, $temp); } //pushing the array in response $response['error'] = false; $response['images'] = $images; break; default: $response['error'] = true; $response['message'] = 'Invalid api call'; } }else{ header("HTTP/1.0 404 Not Found"); echo "<h1>404 Not Found</h1>"; echo "The page that you have requested could not be found."; exit(); } //displaying the response in json header('Content-Type: application/json'); echo json_encode($response); |
- Now we need to test the API.
Testing the API
- For testing our API I am here using POSTMAN, but you can use any tool you want.
- So, the upload call is perfect. Now let’s test the fetch images call.
- It is perfect as well. Now we can jump on Android Side.
Android Upload Image to Server using Volley
Creating an Android Project
- Now, we will create a new Android Studio project with an Empty Activity.
- As we are going to use Volley first, we will add it to the project.
Adding Volley
- Come inside dependencies block of app level build.gradle file and add volley here, as shown below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) compile 'com.android.support:appcompat-v7:26.+' compile 'com.android.support.constraint:constraint-layout:1.0.2' //adding volley library compile 'com.android.volley:volley:1.1.0-rc1' testCompile 'junit:junit:4.12' } |
- Now sync your project.
Adding Permissions
- We also need Internet and Read Storage permission. So inside AndroidManifest.xml add these permissions.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="net.simplifiedlearning.androiduploadimage"> <!-- adding permissions --> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest> |
User Interface
- Now come inside activity_main.xml and put the following xml code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
<?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" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="net.simplifiedlearning.androiduploadimage.MainActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerVertical="true" android:orientation="vertical"> <EditText android:id="@+id/editTextTags" android:hint="Enter tags" android:layout_width="match_parent" android:layout_height="wrap_content" /> <Button android:id="@+id/buttonUploadImage" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:text="Upload Image" /> <ImageView android:id="@+id/imageView" android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout> </RelativeLayout> |
- The above code will generate the following layout.
- The layout is very simple, we have an EditText and a Button only.
Defining EndPoints
- We will create a separate class to store our URL Endpoints. Remember using localhost will not work and you need to find the ip of your xampp. You can find the ip using ipconfig command in windows and ifconfig in linux/mac.
- Create a class named EndPoints and write the following code here. Â
1 2 3 4 5 6 7 8 9 10 11 12 13 |
package net.simplifiedlearning.androiduploadimage; /** * Created by Belal on 10/24/2017. */ public class EndPoints { private static final String ROOT_URL = "http://192.168.101.1/MyApi/Api.php?apicall="; public static final String UPLOAD_URL = ROOT_URL + "uploadpic"; public static final String GET_PICS_URL = ROOT_URL + "getpics"; } |
Volley MultiPart Request
- Here, we need to perform a multipart request. But the problem is volley doesn’t support multipart request directly. So that is why we need our Custom Volley Request. I found this code on GitHub and made little modifications. I am thankful to the author for his contribution.
- Create a class named VolleyMultipartRequest and write the following code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 |
package net.simplifiedlearning.androiduploadimage; import com.android.volley.AuthFailureError; import com.android.volley.NetworkResponse; import com.android.volley.ParseError; import com.android.volley.Request; import com.android.volley.Response; import com.android.volley.VolleyError; import com.android.volley.toolbox.HttpHeaderParser; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.Map; /** * Created by Belal on 10/24/2017. */ public class VolleyMultipartRequest extends Request<NetworkResponse> { private final String twoHyphens = "--"; private final String lineEnd = "\r\n"; private final String boundary = "apiclient-" + System.currentTimeMillis(); private Response.Listener<NetworkResponse> mListener; private Response.ErrorListener mErrorListener; private Map<String, String> mHeaders; public VolleyMultipartRequest(int method, String url, Response.Listener<NetworkResponse> listener, Response.ErrorListener errorListener) { super(method, url, errorListener); this.mListener = listener; this.mErrorListener = errorListener; } @Override public Map<String, String> getHeaders() throws AuthFailureError { return (mHeaders != null) ? mHeaders : super.getHeaders(); } @Override public String getBodyContentType() { return "multipart/form-data;boundary=" + boundary; } @Override public byte[] getBody() throws AuthFailureError { ByteArrayOutputStream bos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(bos); try { // populate text payload Map<String, String> params = getParams(); if (params != null && params.size() > 0) { textParse(dos, params, getParamsEncoding()); } // populate data byte payload Map<String, DataPart> data = getByteData(); if (data != null && data.size() > 0) { dataParse(dos, data); } // close multipart form data after text and file data dos.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd); return bos.toByteArray(); } catch (IOException e) { e.printStackTrace(); } return null; } /** * Custom method handle data payload. * * @return Map data part label with data byte * @throws AuthFailureError */ protected Map<String, DataPart> getByteData() throws AuthFailureError { return null; } @Override protected Response<NetworkResponse> parseNetworkResponse(NetworkResponse response) { try { return Response.success( response, HttpHeaderParser.parseCacheHeaders(response)); } catch (Exception e) { return Response.error(new ParseError(e)); } } @Override protected void deliverResponse(NetworkResponse response) { mListener.onResponse(response); } @Override public void deliverError(VolleyError error) { mErrorListener.onErrorResponse(error); } /** * Parse string map into data output stream by key and value. * * @param dataOutputStream data output stream handle string parsing * @param params string inputs collection * @param encoding encode the inputs, default UTF-8 * @throws IOException */ private void textParse(DataOutputStream dataOutputStream, Map<String, String> params, String encoding) throws IOException { try { for (Map.Entry<String, String> entry : params.entrySet()) { buildTextPart(dataOutputStream, entry.getKey(), entry.getValue()); } } catch (UnsupportedEncodingException uee) { throw new RuntimeException("Encoding not supported: " + encoding, uee); } } /** * Parse data into data output stream. * * @param dataOutputStream data output stream handle file attachment * @param data loop through data * @throws IOException */ private void dataParse(DataOutputStream dataOutputStream, Map<String, DataPart> data) throws IOException { for (Map.Entry<String, DataPart> entry : data.entrySet()) { buildDataPart(dataOutputStream, entry.getValue(), entry.getKey()); } } /** * Write string data into header and data output stream. * * @param dataOutputStream data output stream handle string parsing * @param parameterName name of input * @param parameterValue value of input * @throws IOException */ private void buildTextPart(DataOutputStream dataOutputStream, String parameterName, String parameterValue) throws IOException { dataOutputStream.writeBytes(twoHyphens + boundary + lineEnd); dataOutputStream.writeBytes("Content-Disposition: form-data; name=\"" + parameterName + "\"" + lineEnd); dataOutputStream.writeBytes(lineEnd); dataOutputStream.writeBytes(parameterValue + lineEnd); } /** * Write data file into header and data output stream. * * @param dataOutputStream data output stream handle data parsing * @param dataFile data byte as DataPart from collection * @param inputName name of data input * @throws IOException */ private void buildDataPart(DataOutputStream dataOutputStream, DataPart dataFile, String inputName) throws IOException { dataOutputStream.writeBytes(twoHyphens + boundary + lineEnd); dataOutputStream.writeBytes("Content-Disposition: form-data; name=\"" + inputName + "\"; filename=\"" + dataFile.getFileName() + "\"" + lineEnd); if (dataFile.getType() != null && !dataFile.getType().trim().isEmpty()) { dataOutputStream.writeBytes("Content-Type: " + dataFile.getType() + lineEnd); } dataOutputStream.writeBytes(lineEnd); ByteArrayInputStream fileInputStream = new ByteArrayInputStream(dataFile.getContent()); int bytesAvailable = fileInputStream.available(); int maxBufferSize = 1024 * 1024; int bufferSize = Math.min(bytesAvailable, maxBufferSize); byte[] buffer = new byte[bufferSize]; int bytesRead = fileInputStream.read(buffer, 0, bufferSize); while (bytesRead > 0) { dataOutputStream.write(buffer, 0, bufferSize); bytesAvailable = fileInputStream.available(); bufferSize = Math.min(bytesAvailable, maxBufferSize); bytesRead = fileInputStream.read(buffer, 0, bufferSize); } dataOutputStream.writeBytes(lineEnd); } class DataPart { private String fileName; private byte[] content; private String type; public DataPart() { } DataPart(String name, byte[] data) { fileName = name; content = data; } String getFileName() { return fileName; } byte[] getContent() { return content; } String getType() { return type; } } } |
Upload Image to Server
- Now the last thing is uploading the image, and we will do it inside MainActivity.java.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
package net.simplifiedlearning.androiduploadimage; import android.Manifest; import android.content.Intent; import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.provider.MediaStore; import android.provider.Settings; import android.support.v4.content.ContextCompat; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.EditText; import android.widget.ImageView; import android.widget.Toast; import com.android.volley.AuthFailureError; import com.android.volley.NetworkResponse; import com.android.volley.Request; import com.android.volley.Response; import com.android.volley.VolleyError; import com.android.volley.toolbox.Volley; import org.json.JSONException; import org.json.JSONObject; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.HashMap; import java.util.Map; public class MainActivity extends AppCompatActivity { //ImageView to display image selected ImageView imageView; //edittext for getting the tags input EditText editTextTags; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //initializing views imageView = (ImageView) findViewById(R.id.imageView); editTextTags = (EditText) findViewById(R.id.editTextTags); //checking the permission //if the permission is not given we will open setting to add permission //else app will not open if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.parse("package:" + getPackageName())); finish(); startActivity(intent); return; } //adding click listener to button findViewById(R.id.buttonUploadImage).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { //if the tags edittext is empty //we will throw input error if (editTextTags.getText().toString().trim().isEmpty()) { editTextTags.setError("Enter tags first"); editTextTags.requestFocus(); return; } //if everything is ok we will open image chooser Intent i = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); startActivityForResult(i, 100); } }); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == 100 && resultCode == RESULT_OK && data != null) { //getting the image Uri Uri imageUri = data.getData(); try { //getting bitmap object from uri Bitmap bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), imageUri); //displaying selected image to imageview imageView.setImageBitmap(bitmap); //calling the method uploadBitmap to upload image uploadBitmap(bitmap); } catch (IOException e) { e.printStackTrace(); } } } /* * The method is taking Bitmap as an argument * then it will return the byte[] array for the given bitmap * and we will send this array to the server * here we are using PNG Compression with 80% quality * you can give quality between 0 to 100 * 0 means worse quality * 100 means best quality * */ public byte[] getFileDataFromDrawable(Bitmap bitmap) { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.PNG, 80, byteArrayOutputStream); return byteArrayOutputStream.toByteArray(); } private void uploadBitmap(final Bitmap bitmap) { //getting the tag from the edittext final String tags = editTextTags.getText().toString().trim(); //our custom volley request VolleyMultipartRequest volleyMultipartRequest = new VolleyMultipartRequest(Request.Method.POST, EndPoints.UPLOAD_URL, new Response.Listener<NetworkResponse>() { @Override public void onResponse(NetworkResponse response) { try { JSONObject obj = new JSONObject(new String(response.data)); Toast.makeText(getApplicationContext(), obj.getString("message"), Toast.LENGTH_SHORT).show(); } catch (JSONException e) { e.printStackTrace(); } } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { Toast.makeText(getApplicationContext(), error.getMessage(), Toast.LENGTH_SHORT).show(); } }) { /* * If you want to add more parameters with the image * you can do it here * here we have only one parameter with the image * which is tags * */ @Override protected Map<String, String> getParams() throws AuthFailureError { Map<String, String> params = new HashMap<>(); params.put("tags", tags); return params; } /* * Here we are passing image by renaming it with a unique name * */ @Override protected Map<String, DataPart> getByteData() { Map<String, DataPart> params = new HashMap<>(); long imagename = System.currentTimeMillis(); params.put("pic", new DataPart(imagename + ".png", getFileDataFromDrawable(bitmap))); return params; } }; //adding the request to volley Volley.newRequestQueue(this).add(volleyMultipartRequest); } } |
- That’s it now you can try running your application.
- So upload is working fine. Now the next step is fetching the uploaded images back.
Getting the Images Back to our Application
- We already created the API call to get all the uploaded images.
- Now we can use this URL to get the images back. I have already posted a tutorial about this. So for this part you can visit the below tutorial.
Building a RecyclerView with Images and Text
Android Upload Image to Server Source Code
- If you are having any trouble following the post, then you can get my source code as well.
[sociallocker id=1372] Android Upload Image to Server Source Code [/sociallocker]
So that’s it for this Android Upload Image to Server tutorial friends. If you have any confusion, query or question, just comment it below. And if you think this post is useful please SHARE it with your friends. Thank You 🙂