دانلود فایل با جاوا (کار با فایل ها)

download-in-java

با سلام و عرض ادب خدمت کاربران عزیز با یکی دیگر از آموزش های برنامه نویسی جاوا تحت عنوان کار با فایل در جاوا در خدمت شما عزیزان هستیم. در این جلسه از آموزش قصد داریم دانلود فایل از اینترنت با استفاده از زبان جاوا را در یک مینی پروژه مورد بررسی قرار دهیم.

هدف اصلی این آموزش بررسی نحوه ی کار با فایل در جاوا ، دانلود از اینترنت با استفاده از زبان جاوا و کلاس های آن بوده که در قالب یک مینی پروژه آن را ارائه خواهیم داد.

حداقل پیش نیازهای لازم برای این آموزش  آشنایی اولیه با مباحث OOP و Multithreading در جاوا است که می توانید از آموزش های همین سایت برای یادگیری آن ها استفاده کنید.

کلیه ی کدهای نوشته شده در این بخش در Netbeans 2.8 انجام شده و در محیط Eclipse نیز به راحتی قابل اجرا خواهد بود. در طول آموزش هر جا به کلاس جدیدی برخورد کردیم مختصری درباره آن توضیح خواهیم داد.

ایجاد پروژه

ابتدا یک پروژه ی جدید ایجاد کنید و نامی مناسب برای آن انتخاب نمایید.

سپس یک کلاس جاوا ایجاد کرده و قبل از انجام ادامه ی کار واسط Runnable را برای کلاس implements کنید و متد Run آن را Override و کدهای زیر را در داخل آن قدم به قدم بنویسید.

ابتدا لازم است متغیرهای لازم را در ابتدای برنامه تعریف کنید:

String link;
File out;
String filename;
double filesize;
double percentDownloaded=0.00;

لازم است دو متغیر link و out را در سازنده ی کلاس مقدار دهی کنیم که در ادامه با کاربرد این دو متغیر آشنا خواهیم شد.

Downloader(String link, File out) {
        this.link = link;
        this.out = out;
    }

من در اینجا نام Downloader را برای کلاس در نظر گرفتم. تمامی کدهایی که در ادامه می نویسیم باید در متد Run نوشته شوند.

حال لازم است از کلاس URL یک شی ایجاد کنیم:

URL url = new URL(link);

عبارت link نوشته شده در سازنده ی کلاس در واقع حاوی لینک دانلود فایل مورد نظر ما خواهد بود. همان طور که می دانید عبارت URL مخفف Universal Resource Locator، در واقع حاوی یک صفحه در دنیای وب بوده که به وسیله آن می توان به صفحه مشخصی دست پیدا کرد، در جاوا نیز کلاسی با همین نام وجود دارد که دارای سازنده های مختلفی بوده که ساده ترین آن یک ورودی از نوع String می گیرد که ما نیز در این مثال از آن استفاده کرده ایم. از دیگر سازنده های این کلاس می توان به موارد زیر اشاره کرد:

public URL (java.lang.String protocol, java.lang.String host,java.lang.String file)
public URL (java.lang.String protocol, java.lang.String host,int port, java.lang.String file)
public URL (URL context, String spec)

تذکر: استفاده از کلاس URL  باعث پرتاب خطا می شود بنابراین لازم است این خطای احتمالی با Try-Catch کنترل شود. درنتیجه کل کدهایی که در ادامه نوشته می شوند همگی در داخل بلوک Try قرار می گیرند.

بعد از آن لازم است یک کانکشن بین جاوا و  آدرس صفحه ی مورد نظر ایجاد کنیم پس به صورت زیر عمل می کنیم:

URLConnection http = url.openConnection();

توجه داشته باشید که متد openConnection در کلاس URL وجود دارد و پارامتر بازگشتی آن از نوع URLConnection است. URLConnection یک کلاس انتزاعی بوده و امکان ساختن شئ مستقیم از آن وجود ندارد. خب در حال حاضر کانکشن ما به صفحه ی موردنظر (لینک فایل) باز شده و تمامی ویژگی های آن در شئ http قرار گرفته است.

یکی از مهم ترین ویژگی های یک فایل، حجم آن است که در ادامه با استفاده از متدهایی که جاوا معرفی کرده به آن ها دست پیدا می کنیم.

دستیابی به حجم فایل در جاوا

حجم فایل را در یک متغیر سراسری از نوع double ذخیره می کنیم، این کار با استفاده از متد getContentLength یا getContentLengthLong به صورت زیر انجام خواهد گرفت:

filesize =(double) http.getContentLength()

این دو متد در کلاس انتزاعی URLConnection قرار دارند همچنین دارای پارامتر ورودی نبوده، اما اولی پارامتر خروجی از نوع int و دومی از نوع Long دارد. زمان آن رسیده که نام فایل را از درون لینک دانلود بیرون بکشیم تا در جای مناسب از آن استفاده کنیم پس به این صورت عمل می کنیم:

filename = link.substring(link.lastIndexOf("/")+1);

متغیر filename از نوع String بوده و تعریف اولیه ی آن به صورت سراسری در ابتدای برنامه انجام شده است. دلیل جمع کردن موقعیت آخرین "/" با عدد 1 به منظور ایجاد یک substring بدون در نظر گرفتن کاراکتر "/" است.

ذخیره فایل در کامپیوتر با جاوا

مهم ترین قسمت کار دریافت فایل و ذخیره ی آن بر روی هارد رایانه است. در این مورد ما با دو عمل مختلف روبرو هستیم، یکی از آن ها خواندن فایل از فضای اینترنت و دیگری نوشتن همان جریان داده ی خوانده شده بر روی هارد رایانه است که به صورت زیر عمل می کنیم:

BufferedInputStream bis= new BufferedInputStream(http.getInputStream())
BufferedOutputStream bos= new BufferedOutputStream(new FileOutputStream(out), 1024)

وظیفه ی کلاس BufferedInputStream خواندن جریان ورودی اطلاعات فایل است. ورودی سازنده ی این کلاس از نوع InputStream است به همین منظور با استفاده از متد getInputStream جریان داده را از شئ http دریافت می کنیم.

در مرحله ی بعد لازم است با استفاده از BufferedOutputStream مکان ذخیره ی فایل را بر روی هارد مشخص کنیم سازنده ی این کلاس یک شئ از نوع FileOutputStream می گیرد که یک شی از نوع File بوده و حاوی آدرس محل ذخیره ی فایل است ( شئ out از نوع File است).

به سه متغیر تعریف شده زیر توجه کنید:

byte[] buffer = new byte[1024];
double dowenloaded = 0.00;
int count=0;

لازم است اطلاعات خوانده شده توسط شئ bis به طور موقت در یک بافر با سایز مشخص نوشته شود، به همین دلیل آرایه ای از byte با طول 1024 تعریف کرده ایم تا اطلاعات مرحله به مرحله از bis خوانده و بر روی buffer نوشته شوند. متغیر dowenloaded و count در ادامه ی بحث بررسی خواهد شد.

خب به مرحله ی بسیار مهم خواندن و نوشتن فایل رسیدیم با دقت بسیار زیاد به کدهای نوشته شده در زیر و توضیحات آن توجه کنید:

while((count=bis.read(buffer,0,1024))!=-1){
                bos.write(buffer,0,count);
                dowenloaded+=count;
                percentDownloaded = (dowenloaded*100)/filesize;
                String percent = String.format("%3.2f",percentDownloaded);
                System.out.println("Downloaded "+ percent + "% of file "+ filename);
            }
            bis.close();
            bos.close();
            System.out.println("Download Complate "+out.getAbsolutePath());
            System.exit(0);

با استفاده از یک while و متد read بایت به بایت اطلاعات را خوانده و درون buffer ذخیره می کنیم، پارامتر اول را متد read مشخص می کند که اطلاعات در کجا ذخیره شوند، پارامتر دوم شروع از یک نقطه ی مشخص را برای خواندن اطلاعات اعلام می کند و پارامتر سوم نیز طول اطلاعات را تعیین می نماید.

از آنجایی که طول buffer را 1024 در نظر گرفتیم پس پارامتر سوم هم متناسب با آن انتخاب می کنیم. خروجی متد read از نوع int است و تا زمانی که مقدار آن منفی نشده حلقه به کار خود ادامه خواهد داد. در هر مرحله اجرای حلقه بلافاصله با استفاده از متد write اطلاعات بر روی مکان ذخیره ی فایل نوشته می شوند.

پارامتر اول متد write اطلاعاتی است که باید نوشته شوند و پارامتر دوم نقطه شروع نوشتن اطلاعات را مشخص می کند و پارامتر سوم که در اینجا count در هر مرحله اجرای حلقه تا 1024 شمارش می کند و در خط بعدی مقدار آن به متغیر dowenloaded اضافه می شود تا مقدار دانلود شده ی فایل در هر لحظه مشخص شود. در سه خط بعدی هم طی محاسبات ساده، درصد پیشرفت دانلود را محاسبه و نمایش داده ایم در آخر هم دو شئ bis و bos را close کردیم.

شکل کلی کلاسی که کدهای آن را قدم به قدم تکمیل کردیم به این صورت است:

public class Downloader implements Runnable{
    String link;
    File out;
    String filename;
    double filesize;
    int pos=0;
    double percentDownloaded=0.00;

      Downloader(String link, File out) {
        this.link = link;
        this.out = out;
    }
    @Override
    public void run() {
        try {
            URL url = new URL(link);
            URLConnection http = url.openConnection();
            filesize =(double) http.getContentLengthLong();
            filename = link.substring(link.lastIndexOf("/")+1);
            BufferedInputStream in = new BufferedInputStream(http.getInputStream());
            BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream(out));
            byte[] file = new byte[1024];
            double dowenloaded = 0.00;
            int count=0;
            while((count=in.read(file,0,1024))!=-1){
                bout.write(file,0,count);
                dowenloaded+=count;
                percentDownloaded = (dowenloaded*100)/filesize;
                String percent = String.format("%3.2f",percentDownloaded);
                System.out.println("Downloaded "+ percent + "% of file "+ filename);
            }
            in.close();
            bout.close();
            System.out.println("Download Complate "+out.getAbsolutePath());
            System.exit(0);
            
        } catch (IOException e) {
            System.err.println(e.getMessage());
        }
    }
}

خب حالا لازم است کلاس Main را به این صورت تکمیل کنیم:

    public static void main(String[] args) {
        String link="http://at1.cdn.asandl.com/software/network-internet/download-manager/IDM/Internet.Download.Manager.v6.32.Build.11_AsanDl.com.zip";
        String filename = link.substring(link.lastIndexOf("/")+1);
        File out = new File("D:\\"+filename);
        Downloader downloader = new Downloader(link,out);
        new Thread(downloader).start(); 
    }
}

همان طور که مشاهده می کنید لینک دانلود یک فایل را برای مقداردهی به پارامتر اول سازنده ی کلاس ارسال کرده ایم و در قدم بعدی نام فایل را از داخل لینک جدا کرده تا فایل ذخیره شده همنام فایل اصلی باشد. در خط بعدی با استفاده از کلاس File محل ذخیره سازی را مشخص کردیم و در آخر هم با ساختن شئ از کلاس و مقداردهی به سازنده ی آن Thread آن را Start کردیم تا با اجرای برنامه دانلود فایل آغاز شود. برنامه را اجرا کنید و پروسه ی دانلود فایل را مشاهده کنید.

بررسی چند نکته:

  1. اگر فایل دانلود شده خراب است یا باز نمی شود لازم است کد بخش ذخیره ی فایل (حلقه ی while) را یک بار بررسی کنید.
  2. کدهای نوشته شده در متد Run را می توان در یک متد ساده پیاده سازی و با فراخوانی آن متد عملیات دانلود را آغاز کرد یا به عبارتی می توان مبحث چند نخی را در این کد حذف کرد اما این کار از نظر حرفه ای جایز نیست و در برنامه های بزرگ تر حتما با مشکل جدی و هنگ کردن برنامه مواجه خواهید شد. مثلا اگر برنامه به صورت گرافیکی بود و یک JSlider برای مشخص کردن موقعیت فایل دانلود شده در پروژه وجود داشت (مانند IDM)، مطمئن باشید برنامه تا زمان دانلود کامل فایل با مشکل هنگ کردن مواجه خواهد بود.
  3. برای اضافه کردن بخش دانلود به یک برنامه ی اندرویدی، کد بالا با کمی تغییر قابل استفاده بوده می باشد.

امیدوارم درس کار با فایل در جاوا برای شما عزیزان مفید بوده باشد. موفق و سربلند باشید.

نویسنده شوید

دیدگاه‌های شما

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.