آموزش آپلود عکس با استفاده از کتاب خانه Retrofit
سلام امیدوارم حالتان خوب باشد در این سری از آموزش برنامه نویسی اندروید به آموزش آپلود عکس با استفاده از کتاب خانه Retrofit در این آموزش بعد از انتخاب عکس توسط کاربر عکس به سمت سرور ارسال می شود این عمل توسط php انجام می شود یعنی php در اینجا یک api واسط است که بعد ارسال فایل آن را در جدول مربوط به آن Insert می کند تا بعدا قابل دسترسی باشد در ادامه با ما همراه باشید.
ابتدا باید یک یک جدول به نام images ایجاد کنیم با کد زیر می توانید آن را ایجاد کنید.
1 2 3 4 5 | CREATE TABLE `images` ( `id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT, `description` varchar(1000) NOT NULL, `image` varchar(500) NOT NULL ); |
در بالا هر عکس شامل یک id یک توضیحات . یک url عکس خواهد بود.
شما می توانید این پروژه را در xampp پیاده سازی کنید.
یک فایل php به نام Constants.php ایجاد کرده و کدهای زیر را در آن قرار دهید.
1 2 3 4 5 6 7 8 9 10 11 12 | <?php /** * Created by PhpStorm. * User: JFP * Date: 10/5/2018 * Time: 11:04 PM */ define('DB_HOST', 'localhost'); define('DB_USER', 'root'); define('DB_PASS', 'password'); define('DB_NAME', 'programchi'); define('UPLOAD_PATH', '/uploads/'); |
در بالا باید آدرس های مربوط به دیتابیس خود را در بالا قرار دهید در بالا یک مسیر به نام uploads به صورت پیشفرض برای تعریف شده است.
یک فایل دیگر به نام DbConnect.php ایجاد کرده و کدهای زیر را در آن قرار دهید.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <?php /** * Created by PhpStorm. * User: JFP * Date: 10/5/2018 * Time: 11:31 PM */ 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; } } |
این کد connection بین دیتابیس را با برنامه برقرار می کند.
یک پوشه به نام ImageUploadApi و یک پوشه به نام uploads ایجاد کنید در صورت ایجاد نکردن فایل آپلود نخواهد شد در بعضی موارد ممکنه اتوماتیک پوشه ساخته نشود.
یک فایل دیگر به نام FileHandler.php ایجاد کنید و کدهای زیر را در آن قرار دهید.
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 | <?php /** * Created by PhpStorm. * User: JFP * Date: 10/5/2018 * Time: 11:30 PM */ 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; } } |
کد بالا بعد از آپلود عکس آدرس آن را در table مربوط به آن با یک آیدی خاص و با توضیحات Insert می کند. نکته اگر سایت یا هاست شما https است مقدار http در کد بالا به https تغییر دهید.
یک فایل php دیگر به نام Api.php ایجاد کنید و کدهای زیر را در آن قرار دهید.
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 | <?php /** * Created by PhpStorm. * User: JFP * Date: 10/5/2018 * Time: 11:53 PM */ 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']; } |
کد بالا بعد از اینکه عمل post به آن انجام شد دو object از json برای ما بر می گرداند که همانند زیر است در صورتی که فایل به درستی آپلود شود.
1 2 3 4 | { "error": false, "message": "File Uploaded Successfully" } |
در صورتی که خطا دهید مقدار error برابر با true خواهد بود.
کد php به پایان رسید.
برویم به سرغ بخش کد برنامه
ابتدا باید دو کتاب خانه زیر را به پروژه اضافه کنید دو خط زیر را باید به فایل Build.gradle اضافه کنید.
1 2 | compile 'com.squareup.retrofit2:retrofit:2.3.0' compile 'com.squareup.retrofit2:converter-gson:2.2.0' |
یک کلاس model به نام MyResponse.java ایجاد کنید و کدهای زیر را در آن قرار دهید.
1 2 3 4 5 6 7 8 | package ir.programchi; /** * Created by JFP on 10/5/2018. */ public class MyResponse { boolean error; String message; } |
کدهای بالا همان خروجی های ما در json است.
یک کلاس به نام Api.java ایجاد کنید و کدهای زیر را در آن قرار دهید.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | package ir.programchi; import okhttp3.RequestBody; import okhttp3.ResponseBody; import retrofit2.Call; import retrofit2.http.Multipart; import retrofit2.http.POST; import retrofit2.http.Part; /** * Created by JFP on 10/5/2018. */ public interface Api { String BASE_URL = "http://192.168.43.124/ImageUploadApi/"; @Multipart @POST("Api.php?apicall=upload") Call<MyResponse> uploadImage(@Part("image\"; filename=\"myfile.jpg\" ") RequestBody file, @Part("desc") RequestBody desc); } |
در بالا باید آدرس سایتی که می خواهید بهش request ارسال کنید تا فایل آپلود شود را قرار دهید. بخش های دیگر را تغییر ندهید !
یک لایه به نام activity_main.xml ایجاد کنید و کدهای زیر را در آن قرار دهید.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <?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="ir.programchi"> <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> |
در بالا یک دکمه قرار دارد تا گالری گوشی را باز کند و فایل توسط کاربر انتخاب شود.
وارد فایل AndroidManifes.xml شده و دسترسی های زیر را اضافه کنید.
1 2 | <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> |
دسترسی برای اینترنت و خواندن حافظه
و در نهایت یک در 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 | package ir.programchi; 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); 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; } findViewById(R.id.button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { 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) { Uri selectedImage = data.getData(); uploadFile(selectedImage, "My Image"); } } private void uploadFile(Uri fileUri, String desc) { File file = new File(getRealPathFromURI(fileUri)); RequestBody requestFile = RequestBody.create(MediaType.parse(getContentResolver().getType(fileUri)), file); RequestBody descBody = RequestBody.create(MediaType.parse("text/plain"), desc); Gson gson = new GsonBuilder() .setLenient() .create(); Retrofit retrofit = new Retrofit.Builder() .baseUrl(Api.BASE_URL) .addConverterFactory(GsonConverterFactory.create(gson)) .build(); Api api = retrofit.create(Api.class); Call<MyResponse> call = api.uploadImage(requestFile, descBody); 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(); } }); } 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; } } |
در بالا یک متود به نام getRealPathFromURI وجود دارد که آدرس فایل در گوشی را به دست می آورد. بعد از کلیک بروی دکمه Chosser به نمایش در می آید و شما باید عکس را انتخاب کنید سپس بعد از انتخاب در onActivityResult مسیر دریافت می شود ولی به صورت کلی غیر قابل استفاده است پس با استفاده از getRealPathFromURI آدرس اصلی آن را به دست می آوریم یک متود دیگر به نام uploadFile داریم که عمل آپلود را با استفاده از کتاب خانه Retrofit انجام می دهد.
این آموزش هم به پایان رسید.
موفق و پیروز باشید.
سلام
مرسی از آموزشهاتون
من کدهای بالا رو دقیقا مشابه شما نوشتم ولی response.body().error رو null برمیگردونه
دلیلش چیه ؟
سلام به من این ارور رو برمیگردونه در تست علت ش چیه اگه روی گیت هاب پروژه هست ادرس رو قرا بدید ممنون
java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $
شما میتونید در فایل اینترفیس زمانی که درخوسا رو پست میکنید این دستور رو اضافه کنید
@Headers(“Content-Type: application/json”).
یعنی به این صورت بشه
@Headers(“Content-Type: application/json”)
@Multipart
@POST(“Api.php?apicall=upload”)
Call uploadImage(@Part(“image\”; filename=\”myfile.jpg\” “) RequestBody file, @Part(“desc”) RequestBody desc);