سلام خدمت شما دوستان و همراهان سایت اسمارت دولوپرز . تو این آموزش میخام شما رو با مفاهیم InputStream و OutputStream آشنا کنم و درک این دو مورد رو براتون راحت ترکنم .
InputStream چیست ؟
ما در جاوا برای اینکه بتونیم از یک ورودی اطلاعات رو دریافت کنیم باید یک InpustStream از اون ورودی بگیریم . حالا ورودی ما میخاد یک فایل تکست یا عکس باشه یا یک Socket یا یک کانکشن اینترنت . InputStream زیر کلاس های زیادی داره مثل FileInputStream ، ObjectInputStream ، ByteInputStream و … . که هرکدوم از اینها برای موارد خاصی استفاده میشن .
OutPutStream چیست ؟
برعکس InputStream که برای خوندن اطلاعات نیاز بود ما برای نوشتن اطلاعات به OutPutStream ها نیاز داریم . OutPutStream ها هم متناظر با هر InputStream یک زیر کلاس داره مثل FileOutPutStream ، ObjectOutPutStream و … .
چگونه اطلاعات را از InputStream بخوانیم ؟
کلاس های InputStream همشون متد های ()read برای خودند اطلاعات دارند و سه نوع متد read داریم .
اولیش که هیچ پارامتری دریافت نمیکنه به این صورته :
1 |
inputStream.read() |
این متد ()read میاد دونه دونه بایت هارو از ورودی میخونه ! توجه کنید دونه دونه ! حالا فرض کنید شما یک فایل 50 مگابایتی دارید اینجا باید چند ساعت طول بکشه تا کل فایل رو بخونه ! اما خوشبختانه متد های بعدی ()read اینطوری نیستن .
نسخه بعدی متد ()read که یک پارامتر از جنس آرایه از byte ها میگیره :
1 2 |
byte[] buffer=new byte[1024*4]; inputStream.read(buffer); |
اینجا این متد میاد به اندازه سایز آرایه buffer ینی به اندازه ()buffer.length بایت از ورودی میخونه و توی آرایه buffer ذخیره میکنه . با این روش دیگه چون ما اومدیم مقدار آرایه رو 4Kb ساختیم به اندازه 4 کیلوبایت از ورودی به یکباره میخونه . این متد یک مقدار بازگشتی هم داره که مقدار بازگشتیش تعداد بایت هایی هست که از ورودی خونده .
فقط اینو در نظر داشته باشید که زیاد کردن سایز buffer ممکنه خوب نباشه . چون این اندازه بستگی به سخت افزار و بلاک های مموری و خیلی عوامل دیگه داره که در حال حاضر مناسب ترین عدد همین 4 کیلوبایته . البته ممکنه 16kb و یا 32kb هم گزینه مناسبی باشند .
خب آخرین نسخه متد ()read که سه تا پارامتر میگیره به این صورته :
1 2 3 4 |
//InputStream.read(byte[] buffer,int offset,int length); byte[] buffer=new byte[1024*4]; int len=0; len=inputStream.read(buffer,0,buffer.length()); |
اینجا متد read میاد مقدار length عدد بایت رو از ورودی میخونه و از مکان buffer[offset] شروع به نوشتن داخل آرایه buffer میکنه و در مقدار بازگشتی هم تعداد بایت های خونده شده رو برگشت میده . البته اگه به اندازه length بایت توی ورودی نداشته باشیم مسلما به همون اندازه بایتی که وجود داره میخونه که ممکنه کمتر از مقدار length باشه .
نکته : اما مقدار بازگشتی متد های ()read زمانی که به انتهای فایل رسیده باشیم مقدار1- برگشت میده .
چگونه اطلاعات را در OutputStream بنویسیم ؟
کلاس های OutputStream هم متد هایی برای نوشتن اطلاعات دارند که متناظر با متد های ()read کلاس های InputStream هست .
اولیش متد write ی هست که هیچ پارامتری نمیگیره و دونه دونه بایت هارو داخل خروجی رایت میکنه .
متد بعدی هم که یک پارامتر از جنس آرایه ای از byte ها دریافت میکنه .
1 |
outputStream.write(buffer); |
این متد مقدار بایت های موجود داخل آرایه buffer رو توی خروجی رایت میکنه .
و متد بعدی هم به این صورته :
1 2 |
//write(byte[] buffer, int offset, int lenght) outputStream.write(buffer,offset,langth); |
این متد به اندازه length تعداد بایت رو از مکان offset در آرایه buffer میخونه و توی خروجی رایت میکنه . یعنی چیزی که توی خروجی رایت میشه به این صورته :
1 |
buffer[offset] تا buffer[offset+length] |
این متد رو اگه به این صورت بنویسیم :
1 |
write(buffer, 0, buffer.length) |
برابر این کده :
1 |
write(buffer) |
مثال عملی:
خب الان بیایم با چیزهایی که یاد گرفتیم یک فایل رو به عنوان ورودی بخونیم و توی یه فایل دیگه بنویسیمیش . ( عمل کپی کردن فایل ) .
برای اینکار اول باید یک InputStream از فایل بگیریم و تا زمانی که به انتهای فایل نرسیدیم به خوندن از اون فایل ادامه بدیم و هرچقدر که بایت از ورودی خوندیم رو توی خروجی هم رایت کنیم . برای کارکردن با فایل ها ما از FileInputStream و FileOutputStream استفاده میکنیم .
برای مثال فرض کنید میخایم فایلی با آدرس “/myFolder/file.txt/” رو توی پوشه دیگه ای کپی کنیم مثلا “/folder2/file.txt” .
خب به این صورت باید بنویسیم :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
File sourceFile=new File("/myFolder/file.txt"); File targetFile=new File("/folder2/file.txt"); FileInputStream fileInputStream=null; FileOutputStream fileOutputStream=null; try { fileInputStream=new FileInputStream(sourceFile); fileOutputStream=new FileOutputStream(targetFile); byte[] buffer=new byte[1024*4]; while (fileInputStream.read(buffer)!=-1){ fileOutputStream.write(buffer); } }catch (IOException e) { e.printStackTrace(); }finally { try { if(fileInputStream!=null) fileInputStream.close(); if(fileOutputStream!=null) fileOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } } |
خیلی راحت و بدون هیچ سردرگمی باید متوجه شده باشید که چی شده .
همونطور که گفتم باید تا زمانی که فایل به انتها نرسیده از فایل مقدار buffer.length بایت رو بخونیم و هرچقدر که خوندیم رو توی خروجی هم رایت کنیم . پس میایم از حلقه while استفاده میکنیم و توی شرطش هم مقدار بازگشتی متد ()read رو بررسی میکنیم که اگه تا زمانی که 1- نشده به خوندن ادامه بده و وقتی که به مقدار 1- رسیدیم ینی به آخر اون فایل رسیدیم باید از حلقه خارج شیم . و در نهایت هم باید InputStream ها و OutputStream هارو close کنیم تا منابعی که اشغال کردیم رو رها کنیم تا بقیه هم بتونن از این این منابع استفاده کنند . اما این روش یه مشکلی داره . مثلا فرض کنید ما توی یه فایلی 4 بایت رایت کردیم :
1 |
fileOutputStream.write(new byte[]{1,2,3,4}); |
حالا اگه به روش بالا بخایم از این فایل اطلاعات رو بخونیم و توی یه فایل دیگه رایت کنیم ینی 4 کیلوبایت از این فایل بخونیم ( اینجا چون 4 بایت بیشتر اطلاعات نداره فقط 4 بایت خونده میشه ) و توی آرایه 4 کیلوبایتی buffer قرار بدیم ، این آرایه 4 خونه اولش فقط مقدار داره و بقیش مقدار صفر داره . و اگه بیایم این آرایه رو با استفاده از متد
1 |
fileInputStream.write(buffer); |
توی خروجی رایت کنیم ، اینجا ما یه فایل جدید داریم که حجم فایلش 4 کیلوبایته ، در صورتی که فایل ورودی ما فقط 4 بایت حجم داشت ! به همین منظور ما همیشه برای رایت کردن باید تعداد بایت هایی که از ورودی خونده شده رو نگه داریم و به همون تعداد توی خروجی رایت کنیم . به این کد دقت کنید :
1 2 3 4 5 |
byte[] buffer=new byte[4*1024]; int lenght=0; while((lenght=fileInputStream.read(buffer))!=-1){ fileInputStream.write(buffer,0,lenght); } |
به این ترتیب ما فقط به همون اندازه که بایت خوندیم رو توی خروجی رایت میکنیم و حجم فایل خروجی ما هم فقط 4 بایت خواهد بود .
کپی کردن فایل ها در اندروید :
در اندروید هم روش کپی کردن به همین صورته ولی به دست اوردن InputStream و OutputStream ها فرق میکنه .
اگه شما Uri مبدا و مقصد رو داشته باشید با استفاده از متد openInputStream و openOutputStream کلاس ContentResolver میتونید به ورودی و خروجی دسترسی داشته باشید و متونید از ورودی بخونید و توی خروجی رایت کنید
1 |
getContentResolver().openInputStream(uri); |
و اگه هم آدرس کامل فایل رو دارید که به روش FileInputStream و FileOutputStream میتونید عمل کپی کردن رو انجام بدید . . یا ترکیبی از اینها . مهم داشتن InputStream و OutputStream هست .
نکته : از اونجایی که کار با فایل ها و InputStream ها و OutputStream ها عمل سنگینی هست و باعث بلاک شدن UI میشه باید این عملیات رو داخل Thread ی غیر از main Thread انجام بدید .
خب امید وارم این آموزش براتون مفید واقع شده باشه . منتظر نظرات و پیشنهاداتون هستیم .
عالی بازم
ای ول یعنی Bravo. اشتباهه تو عشقه