رتبه موضوع:
  • 0 رای - 0 میانگین
  • 1
  • 2
  • 3
  • 4
  • 5
ارتباط با سرور و اپلود عکس به سرور (حل شد)
#1
سلام استاد اگه میشه در باره ارتباط با سرور به روش post رو توضیح بدید و سیستم اپلود چطوری باید باشه هم طرف سرور و هم اندروید.
برای اپلود توی وب که با استفاده از $_file استفاده میکردیم این جا باید چیکار کنیم اگه میشه کمکم کنید ممنون.
پاسخ
تشکر شده توسط: Eshpilen
#2
این کلاس آپلودری هست که من برای پروژه خودم نوشتم:

public class UploadManager {
    private String destination;
    private String source;
    private ProgressDialog dialog;

    public UploadManager setSource(String source) {
        this.source = source;
        return this;
    }

    public UploadManager setDestination(String destination) {
        this.destination = destination;
        return this;
    }

    public UploadManager setDialog(ProgressDialog dialog) {
        this.dialog = dialog;
        return this;
    }

    public String upload() {
        final String[] result = {""};
        final File sourceFile = new File(source);
        if (!sourceFile.isFile()) {
            dialog.dismiss();
        } else {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        HttpURLConnection connection;
                        DataOutputStream dataOutputStream;
                        String lineEnd = "rn";
                        String twoHyphens = "--";
                        String boundary = "*****";
                        int bytesAvailable, bufferSize;
                        byte[] buffer;

                        FileInputStream fileInputStream = new FileInputStream(sourceFile);

                        URL url = new URL(App.URL_UPLOAD);

                        connection = (HttpURLConnection) url.openConnection();
                        connection.setDoInput(true);
                        connection.setDoOutput(true);
                        connection.setUseCaches(false);
                        connection.setChunkedStreamingMode(App.BUFFER_SIZE);
                        connection.setRequestMethod("POST");
                        connection.setRequestProperty("Connection", "Keep-alive");
                        connection.setRequestProperty("Cache-Control", "no-cache");
                        connection.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);
                        connection.setRequestProperty("photo", destination);

                        dataOutputStream = new DataOutputStream(connection.getOutputStream());
                        dataOutputStream.writeBytes(twoHyphens + boundary + lineEnd);
                        dataOutputStream.writeBytes("Content-Disposition: form-data; name="photo";filename="" + destination + """ + lineEnd);
                        dataOutputStream.writeBytes(lineEnd);

                        bytesAvailable = fileInputStream.available();
                        bufferSize = Math.min(bytesAvailable, App.BUFFER_SIZE);
                        buffer = new byte[bufferSize];
                        while (fileInputStream.read(buffer, 0, bufferSize) > 0) {
                            dataOutputStream.write(buffer, 0, bufferSize);
                            bytesAvailable = fileInputStream.available();
                            bufferSize = Math.min(bytesAvailable, App.BUFFER_SIZE);
                        }

                        dataOutputStream.writeBytes(lineEnd);
                        dataOutputStream.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd);

                        fileInputStream.close();
                        dataOutputStream.flush();
                        dataOutputStream.close();

                        connection.connect();
                        InputStream inputStream = connection.getInputStream();
                        result[0] = HelperString.convertInputStreamToString(inputStream);

                    } catch (FileNotFoundException e) {
                        e.printStackTrace();
                    } catch (ProtocolException e) {
                        e.printStackTrace();
                    } catch (MalformedURLException e) {
                        e.printStackTrace();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });

            thread.start();

            while (thread.isAlive()) ;
        }

        return result[0];
    }
}

نقل قول:توضیح: App.URL_UPLOAD آدرس اسکریپتی در سرور هست که وظیفه مدیریت فایلهای آپلودشده رو بعهده داره. مثلاً mysite.com/upload.php با محتوای زیر:
if (isset($_FILES['photo'])) {
    $photo = & $_FILES['photo'];
    if ($photo['error'] == 0) {
        if($im = ImageCreateFromJPEG($photo['tmp_name'])) {
            $fileNameParts = explode('.', $photo['name']);
            $result = ['code' => (ImageJPEG($im, 'photos/' . $fileNameParts[0] . '.jpg', 100) ? 1 : 0)];
        } else {
            $result = ['code' => 0];
        }
    }
}

App.BUFFER_SIZE هم میزان بافر برای انتقال اطلاعات هست که من 8 کیلوبایت درنظر گرفتم (8 * 1024 = 8192)

یه نکته دیگه اینکه setSource برای مشخص‌کردن مسیر فیزیکی فایل مبدأ و setDestination برای مشخص‌کردن اسمی که میخوایم فایل رو تحت اون آپلود کنیم (و توی سرور استفاده بشه) بکار میره.

بعنوان مثالی از نحوه استفاده، کلاس Async زیر رو درنظر بگیرین که عکس انتخابی رو آپلود میکنه:
public class AsyncUploadPhoto extends AsyncTask<String, String, String> {
    private String source;
    private String destination;
    private ProgressDialog progressDialog;

    public AsyncUploadPhoto(String source, String destination) {
        this.source = source;
        this.destination = destination;
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        progressDialog = new ProgressDialog(App.currentActivity);
        progressDialog.setTitle("آپلود عکس به سرور");
        progressDialog.setMessage("لطفاً صبر کنید...");
        progressDialog.show();
    }

    @Override
    protected String doInBackground(String... strings) {
       return new UploadManager()
                .setSource(source)
                .setDestination(destination)
                .setDialog(progressDialog)
                .upload();
    }

    @Override
    protected void onPostExecute(String s) {
        super.onPostExecute(s);
        try {
            JSONObject json = new JSONObject(s);
            int code = json.getInt("code");
            if (code == 1) {
                Toast.makeText(App.context, "تصویر با موفقیت روی سرور آپلود شد.", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(App.context, "در زمان آپلود تصویر به سرور خطایی رخ داد. مجدداً تلاش کنید.", Toast.LENGTH_SHORT).show();
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
        progressDialog.dismiss();

    }
}

و درنهایت نحوه استفاده از کلاس AsyncUploadPhoto برای اینکه تصویر در پس‌زمینه آپلود بشه (توی یک Thread جدا) تا برنامه اصلی موقع آپلود و گرفتن جواب از سرور هنگ نکنه:
new AsyncUploadLogo(imagePath, "photo.jpg").execute();

نقل قول:نکته: imagePath مسیری فیزیکی عکس روی موبایل هست و photo.jpg هم اسمی که میخوایم تحت اون نام برای سرور آپلود بشه (این اسم توی سرور بعنوان نام فایلی که برای آپلود انتخاب شده درنظر گرفته میشه. درست مثل اینکه کاربر توی فرم HTML فایل photo.jpg رو انتخاب کرده باشه).

اگه سؤالی بود در خدمتم. هر جاش رو متوجه نشدین بپرسین. فقط قبلش خط به خط بخونید و مطمئن بشین که متوجه نمیشین چون سعی کردم خیلی خوانا بنویسم.
پاسخ
تشکر شده توسط: balutsoft , Eshpilen
#3
خوب مشکلم اینجاست که چطوری با intent عکس رو انتخاب کنم و ادرسش رو بگیرم.
پاسخ
تشکر شده توسط:
#4
برای این کار باید از Intent انتخاب عکس از گالری استفاده کنید:
اول یه ثابت توی کلاس تعریف کنید به این شکل:
private static final int PICK_GALLERY = 1;

بعد این کد رو برای دکمه انتخاب عکس از گالری بنویسید:
btnGallery.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        Intent pickIntent = new Intent(Intent.ACTION_PICK);
        pickIntent.setType("image/*");
        startActivityForResult(pickIntent, PICK_GALLERY);
    }
});

حالا میتونید توی onActivityResult جواب دریافتی رو پردازش کنید:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == PICK_GALLERY) {
        if (resultCode == RESULT_OK && data != null) {
            picUri = data.getData();
            imagePath = HelperString.getRealPathFromUri(picUri);
            String imagePathIndex = MediaStore.Images.Media.DATA;
            Cursor cursor = getContentResolver().query(picUri, new String[]{imagePathIndex}, null, null, null);
            cursor.moveToFirst();
            String fileName = cursor.getString(cursor.getColumnIndex(imagePathIndex));
            imgLogo.setImageBitmap(BitmapFactory.decodeFile(fileName));
        }
    }
}

کد متد getRealPathFromUri از کلاس HelperString
public static String getRealPathFromUri(Uri uri) {
    String result = "";
    String documentID;
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
        String[] pathParts = uri.getPath().split("/");
        documentID = pathParts[pathParts.length - 1];
    } else {
        String pathSegments[] = uri.getLastPathSegment().split(":");
        documentID = pathSegments[pathSegments.length - 1];
    }
    String mediaPath = MediaStore.Images.Media.DATA;
    Cursor imageCursor = App.currentActivity.getContentResolver().query(uri, new String[]{mediaPath}, MediaStore.Images.Media._ID + "=" + documentID, null, null);
    if (imageCursor.moveToFirst()) {
        result = imageCursor.getString(imageCursor.getColumnIndex(mediaPath));
    }
    return result;
}
پاسخ
تشکر شده توسط: balutsoft
#5
خود HelperString رو من نمیبینم؟!!!
پاسخ
تشکر شده توسط:
#6
یه کلاس بسازین و این متد رو توش بنویسید دیگه. من چون توی کلاسم چندتا متد دیگه هم داشتم ننوشتم.
پاسخ
تشکر شده توسط:
#7
اخه به این بخش ارور میده و اگه این کلاس رو بنویسم convertInputStreamToString این متد رو ندارم من.
پاسخ
تشکر شده توسط:
#8
اینم واسه اینکه نخواین دنبالش بگردین:

public class HelperString {
    public static String getRealPathFromUri(Uri uri) {
        String result = "";
        String documentID;
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
            String[] pathParts = uri.getPath().split("/");
            documentID = pathParts[pathParts.length - 1];
        } else {
            String pathSegments[] = uri.getLastPathSegment().split(":");
            documentID = pathSegments[pathSegments.length - 1];
        }
        String mediaPath = MediaStore.Images.Media.DATA;
        Cursor imageCursor = App.currentActivity.getContentResolver().query(uri, new String[]{mediaPath}, MediaStore.Images.Media._ID + "=" + documentID, null, null);
        if (imageCursor.moveToFirst()) {
            result = imageCursor.getString(imageCursor.getColumnIndex(mediaPath));
        }
        return result;
    }

    public static String convertInputStreamToString(InputStream inputStream) {
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            StringBuilder builder = new StringBuilder();

            String line = "";

            while ((line = reader.readLine()) != null) {
                builder.append(line);
            }

            return builder.toString();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }
}
پاسخ
تشکر شده توسط: balutsoft
#9
یکی از دوستان توی گروه تلگرام http://yon.ir/androidj گفتن که خطای NullPointerException بخاطر App.currentActivity گرفتن. برای رفع این خطا باید کلاس App رو به پروژه اضافه کنید:
class App extends Application {
    public static Activity currentActivity;
}

بعد توی مانیفست معرفی کنید:
<application
    android:name=".App"
    ...

بعد یه کلاس از Activity یا AppCompactActivity (برحسب اینکه اکتیویتی شما از چه کلاسی مشتق شده) مشتق کنید:
public class ActivityEnhanced extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        App.currentActivity = this;
    }

    @Override
    public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
        super.onCreate(savedInstanceState, persistentState);
        App.currentActivity = this;
    }

    @Override
    protected void onResume() {
        super.onResume();
        App.currentActivity = this;
    }
}

حالا تمام اکتیویتیهای خودتون رو از کلاس ActivityEnhanced مشتق کنید.
پاسخ
تشکر شده توسط: balutsoft
#10
نقل قول:
connection.setRequestProperty("photo", destination);
این دیگه واسه چیه؟ Huh
پاسخ
تشکر شده توسط:
#11
برای اینکه اسم فایل ارسالی توی هدر درخواست قرار بگیره.
پاسخ
تشکر شده توسط:
#12
(17-03-1395، 08:58 ب.ظ)ADMIN نوشته: برای اینکه اسم فایل ارسالی توی هدر درخواست قرار بگیره.
توی هدر قرار بگیره که چی بشه؟ Huh
پاسخ
تشکر شده توسط:
#13
بعضی جاها نیاز بهش احساس میشه. مثلاً من خودم توی بحث آپلود فایل و ارتباط بین موبایل و وب‌سرویس اومدم برای اینکه متوجه بشم فایلی وجود داره یا نه، هدر رو چک کردم. درواقع ابتدا هدر رو بررسی میکردم و محتوا رو درخواست نمیکردم (که حجم خیلی کمتری داره نسبت به حالتی که بدنه هم درخواست بشه). بعد اگه میدیدم فایل همراهش هست بدنه رو درخواست میکردم. بهرحال اجباری نیست ولی ممکنه توی سناریوی خاصی بدرد بخوره که روش ست کردن هدرش رو گفتم.
پاسخ
تشکر شده توسط:




کاربران در حال بازدید این موضوع: 1 مهمان