سلام دوستان در این مقاله نحوه ارسال تصاویر در یک اپلیکیشن اندروید آموزش داده می شود. برای انجام این کار از کتابخانه Retrofit استفاده خواهیم کرد . این کتابخانه به ما این امکان را می دهد چندین درخواست را به سرور ارسال کنیم و همچنین از آن برای آپلود کردن فایل ها از اپلیکیشن به سرور استفاده کنیم . خوب بریم سراغ آموزش و ساخت پروژه . همچنین کد های سمت سرور این پروژه با PHP و پایگاه داده MySQL می باشد. با آموزش استفاده از کتابخانه Retrofit برای ارسال تصاویر در اپلیکیشن اندروید با ما همراه باشید .
فهرست:
۱.ساختمان APIs
- ایجاد پایگاه داده MySQL
- ایجاد پروژه PHP
- تعریف ثابت
- اتصال به پایگاه داده
- دایرکتوری ارسال
- بررسی فایل ارسال و دانلود
- مدیریت فرمان API
۲. آموزش Upload فایل با Retrofit
- ایجاد یک پروژه جدید
- افزودن کتابخانه ها
- ایجاد کلاس مدل برای پاسخ
- ایجاد رابط API
- آپلود فایل
- ایجاد رابط کاربری
- بررسی مجوز خواندن ذخیره سازی
- انتخاب یک فایل
- گرفتن مسیر مطلق از Uri
- ارسال فایل
۳.بارگیری تصاویر برگشت
۴.بارگیری مجدد فایل کد منبع
ساخت APIs
در این اپلیکیشن قصد داریم فایلی را به سرور ارسال کنیم . بنابراین در سمت سرور باید فایل را بگیریم و آنرا روی سرور ذخیره کنیم . برای انجام این وظایف API ایجاد می کنیم. در اینجا از سرور XAMPP استفاده می کنیم . شما میتوانید از هر سرور دیگری استفاده کنید.
ایجاد پایگاه داده MySQL
اولین گام ایجاد پایگاه داده است و ما به یک پایگاه داده که در زیر نمایش داده شده نیاز داریم
پایگاه داده
CREATE TABLE `images` ( `id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT, `description` varchar(1000) NOT NULL, `image` varchar(500) NOT NULL );
ایجاد پروژه PHP
برای ایجاد پروژه php در اینجا از PHP Storm استفاده می کنیم .اما شما می توانید از notepad++, sublime و چیز دیگری استفاده کنید.
یک پروژه جدید با نام ImageUploadApi داخل پوشه c:/xampp/htdocs ) htdocs ) ایجاد می کنیم.
تعریف ثابت ها
ابتدا یک فایل با نام Constants.php برای تعریف تمام ثابتهای مورد نیاز ایجاد می کنیم.
<?php /** * Created by PhpStorm. * User: Belal * Date: 10/5/2017 * Time: 11:31 AM */ define('DB_HOST', 'localhost'); define('DB_USER', 'root'); define('DB_PASS', 'password'); define('DB_NAME', 'simplifiedcoding'); define('UPLOAD_PATH', '/uploads/');
اتصال به پایگاه داده
حالا می خواهیم به پایگاه داده مان متصل شویم . برای این منظور یک کلاس PHP به نام DbConnect.php ایجاد می کنیم و کدهای زیر را می نویسیم.
<?php /** * Created by PhpStorm. * User: Belal * Date: 10/5/2017 * Time: 11:31 AM */ class DbConnect { private $con; public function connect() { require_once dirname(__FILE__) . '/Constants.php'; $this->con = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME); if (mysqli_connect_errno()) { echo 'Failed to connect ' . mysqli_connect_error(); return null; } return $this->con; } }
پوشه آپلود
یک پوشه درون پروژه خود با نام uploadsایجاد کنید.
در این پوشه تمام تصاویر آپلود شده توسط کاربر را ذخیره خواهیم کرد.
دوره آموزش اندروید
ذخیره و بازیابی فایل آپلود و دانلود
حالا ما یک کلاس دیگر با نام FileHandler.php برای ذخیره و بازیابی تصاویر ایجاد می کنیم
<?php class FileHandler { private $con; public function __construct() { require_once dirname(__FILE__) . '/DbConnect.php'; $db = new DbConnect(); $this->con = $db->connect(); } public function saveFile($file, $extension, $desc) { $name = round(microtime(true) * 1000) . '.' . $extension; $filedest = dirname(__FILE__) . UPLOAD_PATH . $name; move_uploaded_file($file, $filedest); $url = $server_ip = gethostbyname(gethostname()); $stmt = $this->con->prepare("INSERT INTO images (description, url) VALUES (?, ?)"); $stmt->bind_param("ss", $desc, $name); if ($stmt->execute()) return true; return false; } public function getAllFiles() { $stmt = $this->con->prepare("SELECT id, description, url FROM images ORDER BY id DESC"); $stmt->execute(); $stmt->bind_result($id, $desc, $url); $images = array(); while ($stmt->fetch()) { $temp = array(); $absurl = 'http://' . gethostbyname(gethostname()) . '/ImageUploadApi' . UPLOAD_PATH . $url; $temp['id'] = $id; $temp['desc'] = $desc; $temp['url'] = $absurl; array_push($images, $temp); } return $images; } }
API مدیریت فرمانها
برای اینکار یک فایل PHP با نام Api.php ایجاد کرده و تمامی فرمانهای API را کنترل خواهیم کرد.
<?php require_once dirname(__FILE__) . '/FileHandler.php'; $response = array(); if (isset($_GET['apicall'])) { switch ($_GET['apicall']) { case 'upload': if (isset($_POST['desc']) && strlen($_POST['desc']) > 0 && $_FILES['image']['error'] === UPLOAD_ERR_OK) { $upload = new FileHandler(); $file = $_FILES['image']['tmp_name']; $desc = $_POST['desc']; if ($upload->saveFile($file, getFileExtension($_FILES['image']['name']), $desc)) { $response['error'] = false; $response['message'] = 'File Uploaded Successfullly'; } } else { $response['error'] = true; $response['message'] = 'Required parameters are not available'; } break; case 'getallimages': $upload = new FileHandler(); $response['error'] = false; $response['images'] = $upload->getAllFiles(); break; } } echo json_encode($response); function getFileExtension($file) { $path_parts = pathinfo($file); return $path_parts['extension']; }
اکنون APIمان را تست می کنیم و برای اینکار می توانیم از REST Client استفاده کنیم.در اینجا ازPOSTMAN استفاده شده است.
تست API
API خوب کار می کند.حالا به بخش اندروید برویم.
آموزش آپلود فایل با کتابخانه Retrofit
قسمت اصلی از اینجا شروع می شود.گام اول همیشه ایجاد یک پروژه جدید اندروید می باشد.
ایجاد یک پروژه جدید
یک پروژه جدید با نام RetrofitFileUpload ایجاد می کنیم.
زمانی که پروژه به طور کامل بارگذاری شد Retrofitو GSON را به آن اضافه می کنیم.
افزودن کتابخانه ها
داخل فایل build.gradle برنامه شوید و خطوط داده شده زیر را در بلوک dependencies وارد نمایید.
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' //these two lines are added compile 'com.squareup.retrofit2:retrofit:2.3.0' compile 'com.squareup.retrofit2:converter-gson:2.2.0' testCompile 'junit:junit:4.12' }
اکنون پروژه خود را هماهنگ (sync) کنید
ایجاد کلاس مدل برای پاسخ
پاسخی که ما از سرور بعد از آپلود فایل دریافت می کنیم :
{ "error": false, "message": "File Uploaded Successfullly" }
برای خواندن پاسخ با لا از سرور یک کلاس مدل ایجاد خواهیم کرد.یک کلاس با نام MyResponse.java ایجاد کرده و کدهای زیر را می نویسیم.
package net.simplifiedlearning.retrofitfileupload; public class MyResponse { boolean error; String message; }
ایجاد رابط API
حالا به یک رابط برای تعریف همه فرمانهای API نیاز داریم.پس یک رابط با نام Api.java ایجاد کرده و کدهای زیر را می نویسیم.
package net.simplifiedlearning.retrofitfileupload; import okhttp3.RequestBody; import okhttp3.ResponseBody; import retrofit2.Call; import retrofit2.http.Multipart; import retrofit2.http.POST; import retrofit2.http.Part; public interface Api { //the base URL for our API //make sure you are not using localhost //find the ip usinc ipconfig command String BASE_URL = "http://192.168.43.124/ImageUploadApi/"; //this is our multipart request //we have two parameters on is name and other one is description @Multipart @POST("Api.php?apicall=upload") Call<MyResponse> uploadImage(@Part("image\"; filename=\"myfile.jpg\" ") RequestBody file, @Part("desc") RequestBody desc); }
آپلود کردن فایل
ایجاد رابط کاربری
برای ارسال وگرفتن یک فایل ما به یک فایل انتخابگر نیاز داریم . بنابراین یک دکمه ایجاد می کنیم وبا ضربه زدن بر این دکمه اکتیویتی فایل انتخابگر را باز می کنیم.
یک دکمه درون activity_main.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" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="net.simplifiedlearning.retrofitfileupload.MainActivity"> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_centerVertical="true" android:text="Upload Image" /> </RelativeLayout>
بررسی خواندن مجوز ذخیره سازی
در صورتیکه اپلیکیشن از ورژن بالاتر Lollipop اجرا شود پیغام مجوز دسترسی از ما درخواست خواهد شد اما همچنین باید مجوز مورد نیاز را در فایل manifest نیز وارد کنیم . مجوزی که اینجا لازم است مربوط به خواندن می باشد تا از آن برای آپلود تصاویر استفاده کنیم . ایتدا ما باید بتوانیم از مکان ذخیره سازی تصاویر فایل ها را بخوانیم و سپس فایل را آپلود کنیم .
پس فایل AndroidManifest.xml را باز کنید و مجوز خواندن را تعریف کنید.
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="net.simplifiedlearning.retrofitfileupload"> <!-- read and internet permission --> <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>
مجوز اینترنت را به این صورت تعریف کردیم زیرا به آن نیاز داریم.اما چون نیازی به مجوز اینترنت در زمان اجرا نداریم.پس در onCreate() کد زیر را اضافه می کنیم.
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; }
اگر برنامه مجوز خواندن از ذخیره ساز را نداشته باشد کد بالا برنامه را متوقف می کند و صفحه تنظیمات را باز می کند.
انتخاب یک فایل
ما کدهای زیر را در رویداد کلیک دکمه اضافه میکنیم برای اینکه فایل انتخابگر را باز کنیم.
Intent i = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); startActivityForResult(i, 100);
هم اکنون باید نتیجه این گزینه فایل انتخابگر را پیگیری کنیم. برای انجام اینکار باید متد () onActivityResultرا Override کنیم.
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == 100 && resultCode == RESULT_OK && data != null) { //the image URI Uri selectedImage = data.getData(); } }
ما نشانی اینترنتی تصویر را داریم اما کافی نیست . ما به مسیر واقعی تصویر نیاز داریم . برای اینکار یک متد دیگر ایجاد می کنیم.
گرفتن مسیر واقعی از Uri
برای گرفتن مسیر واقعی از متد زیر استفاده میکنیم.
/* * This method is fetching the absolute path of the image file * if you want to upload other kind of files like .pdf, .docx * you need to make changes on this method only * Rest part will be the same * */ private String getRealPathFromURI(Uri contentUri) { String[] proj = {MediaStore.Images.Media.DATA}; CursorLoader loader = new CursorLoader(this, contentUri, proj, null, null, null); Cursor cursor = loader.loadInBackground(); int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); cursor.moveToFirst(); String result = cursor.getString(column_index); cursor.close(); return result; }
آپلود فایل
حالا برای آپلود کردن فایل متد ()upLoadFile را ایجاد می کنیم.
private void uploadFile(Uri fileUri, String desc) { //creating a file File file = new File(getRealPathFromURI(fileUri)); //creating request body for file RequestBody requestFile = RequestBody.create(MediaType.parse(getContentResolver().getType(fileUri)), file); RequestBody descBody = RequestBody.create(MediaType.parse("text/plain"), desc); //The gson builder Gson gson = new GsonBuilder() .setLenient() .create(); //creating retrofit object Retrofit retrofit = new Retrofit.Builder() .baseUrl(Api.BASE_URL) .addConverterFactory(GsonConverterFactory.create(gson)) .build(); //creating our api Api api = retrofit.create(Api.class); //creating a call and calling the upload image method Call<MyResponse> call = api.uploadImage(requestFile, descBody); //finally performing the call call.enqueue(new Callback<MyResponse>() { @Override public void onResponse(Call<MyResponse> call, Response<MyResponse> response) { if (!response.body().error) { Toast.makeText(getApplicationContext(), "File Uploaded Successfully...", Toast.LENGTH_LONG).show(); } else { Toast.makeText(getApplicationContext(), "Some error occurred...", Toast.LENGTH_LONG).show(); } } @Override public void onFailure(Call<MyResponse> call, Throwable t) { Toast.makeText(getApplicationContext(), t.getMessage(), Toast.LENGTH_LONG).show(); } }); }
تمام کدهای MainActivity.java به شرح ذیل است :
package net.simplifiedlearning.retrofitfileupload; import android.Manifest; import android.content.Intent; import android.content.pm.PackageManager; import android.database.Cursor; import android.net.Uri; import android.os.Build; import android.provider.MediaStore; import android.provider.Settings; import android.support.v4.content.ContextCompat; import android.support.v4.content.CursorLoader; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Toast; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import java.io.File; import java.io.IOException; import okhttp3.MediaType; import okhttp3.RequestBody; import okhttp3.ResponseBody; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; import retrofit2.Retrofit; import retrofit2.converter.gson.GsonConverterFactory; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //checking the permission 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.button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { //opening file 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) { //the image URI Uri selectedImage = data.getData(); //calling the upload file method after choosing the file uploadFile(selectedImage, "My Image"); } } private void uploadFile(Uri fileUri, String desc) { //creating a file File file = new File(getRealPathFromURI(fileUri)); //creating request body for file RequestBody requestFile = RequestBody.create(MediaType.parse(getContentResolver().getType(fileUri)), file); RequestBody descBody = RequestBody.create(MediaType.parse("text/plain"), desc); //The gson builder Gson gson = new GsonBuilder() .setLenient() .create(); //creating retrofit object Retrofit retrofit = new Retrofit.Builder() .baseUrl(Api.BASE_URL) .addConverterFactory(GsonConverterFactory.create(gson)) .build(); //creating our api Api api = retrofit.create(Api.class); //creating a call and calling the upload image method Call<MyResponse> call = api.uploadImage(requestFile, descBody); //finally performing the call call.enqueue(new Callback<MyResponse>() { @Override public void onResponse(Call<MyResponse> call, Response<MyResponse> response) { if (!response.body().error) { Toast.makeText(getApplicationContext(), "File Uploaded Successfully...", Toast.LENGTH_LONG).show(); } else { Toast.makeText(getApplicationContext(), "Some error occurred...", Toast.LENGTH_LONG).show(); } } @Override public void onFailure(Call<MyResponse> call, Throwable t) { Toast.makeText(getApplicationContext(), t.getMessage(), Toast.LENGTH_LONG).show(); } }); } /* * This method is fetching the absolute path of the image file * if you want to upload other kind of files like .pdf, .docx * you need to make changes on this method only * Rest part will be the same * */ private String getRealPathFromURI(Uri contentUri) { String[] proj = {MediaStore.Images.Media.DATA}; CursorLoader loader = new CursorLoader(this, contentUri, proj, null, null, null); Cursor cursor = loader.loadInBackground(); int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); cursor.moveToFirst(); String result = cursor.getString(column_index); cursor.close(); return result; } }
حالا برنامه خود را اجرا کرده و آن را امتحان کنید .