(1) مبدأ التنزيل القابل للاستئناف <BR> في الواقع، مبدأ التنزيل القابل للاستئناف بسيط للغاية. الأمر الوحيد هو أن طلب HTTP يختلف عن التنزيل العام.
على سبيل المثال، عندما يطلب المتصفح ملفًا على الخادم، يكون الطلب الذي يقدمه كما يلي:
افترض أن اسم مجال الخادم هو www.sjtu.edu.cn واسم الملف هو down.zip.
احصل على /down.zip HTTP/1.1
قبول: صورة/gif، صورة/x-xbitmap، صورة/jpeg، صورة/pjpeg، application/vnd.ms-
إكسل، تطبيق/msword، تطبيق/vnd.ms-powerpoint، */*
قبول اللغة: zh-cn
قبول الترميز: gzip، انكماش
وكيل المستخدم: Mozilla/4.0 (متوافق؛ MSIE 5.01؛ Windows NT 5.0)
الاتصال: البقاء على قيد الحياة
بعد أن يتلقى الخادم الطلب، يقوم بالبحث عن الملف المطلوب بالشكل المطلوب، ويستخرج معلومات الملف، ثم يعيده إلى المتصفح، وتكون المعلومات التي تم إرجاعها كما يلي
200
طول المحتوى=106786028
قبول النطاقات = بايت
التاريخ=الاثنين، 30 أبريل 2001، الساعة 12:56:11 بتوقيت جرينتش
ETag=W/"02ca57e173c11:95b"
نوع المحتوى = التطبيق/الثمانية تيار
الخادم = Microsoft-IIS/5.0
تاريخ آخر تعديل=الاثنين، 30 أبريل 2001، الساعة 12:56:11 بتوقيت جرينتش
يعني ما يسمى بتنزيل السيرة الذاتية مواصلة التنزيل من النقطة التي تم تنزيل الملف فيها. حتى في تمرير متصفح العميل
عندما يتعلق الأمر بخوادم الويب، يجب إضافة معلومة أخرى - من أين نبدأ.
ما يلي هو "متصفح" قمت بتجميعه بنفسي لتمرير معلومات الطلب إلى خادم الويب. يبدأ الطلب من 2000070 بايت.
احصل على /down.zip HTTP/1.0
وكيل المستخدم: NetFox
النطاق: بايت = 2000070-
قبول: نص/html، صورة/gif، صورة/jpeg، *; q=.2, */*;
إذا نظرت بعناية، ستجد سطرًا إضافيًا من RANGE: bytes=2000070-
معنى هذا السطر هو إخبار الخادم بأن الملف down.zip سيتم نقله بدءًا من 2000070 بايت، ولا يلزم نقل البايتات السابقة.
بعد أن يتلقى الخادم هذا الطلب، تكون المعلومات التي تم إرجاعها كما يلي:
206
طول المحتوى=106786028
نطاق المحتوى = بايت 2000070-106786027/106786028
التاريخ=الاثنين، 30 أبريل 2001، الساعة 12:55:20 بتوقيت جرينتش
ETag=W/"02ca57e173c11:95b"
نوع المحتوى = التطبيق/الثمانية تيار
الخادم = Microsoft-IIS/5.0
تاريخ آخر تعديل=الاثنين، 30 أبريل 2001، الساعة 12:55:20 بتوقيت جرينتش
وبمقارنتها بالمعلومات التي أعادها الخادم السابق، ستجد أنه تمت إضافة سطر:
نطاق المحتوى = بايت 2000070-106786027/106786028
كما تم تغيير رمز الإرجاع إلى 206 بدلاً من 200.
بمعرفة المبادئ المذكورة أعلاه، يمكنك برمجة استئناف تنزيل نقطة التوقف.
(2) النقاط الرئيسية في تنفيذ نقل استئناف نقطة التوقف في Java
(1) ما هي الطريقة المستخدمة لإرسال النطاق: bytes=2000070-.
بالطبع، يمكن بالتأكيد القيام بذلك باستخدام المقبس الأصلي، ولكن هذا سيكون مزعجًا للغاية، في الواقع، توفر حزمة Java net هذه الوظيفة. الرمز هو كما يلي:
URL url = عنوان URL الجديد("http://www.sjtu.edu.cn/down.zip");
HttpURLConnection httpConnection = (HttpURLConnection)url.openConnection();
//تعيين وكيل المستخدم
httpConnection.setRequestProperty("User-Agent"،"NetFox");
// قم بتعيين موضع البداية لاستئناف إرسال نقطة التوقف
httpConnection.setRequestProperty("RANGE"،"bytes=2000070");
// احصل على دفق الإدخال
InputStream input = httpConnection.getInputStream();
دفق البايت المأخوذ من دفق الإدخال هو دفق البايت الذي يبدأ من 2000070 في ملف down.zip.
كما ترى، من السهل جدًا تنفيذ نقل السيرة الذاتية لنقطة التوقف في Java.
والشيء التالي الذي يجب فعله هو كيفية حفظ الدفق الذي تم الحصول عليه في ملف.
الطريقة المستخدمة لحفظ الملف .
أستخدم فئة RandAccessFile في حزمة IO.
العملية بسيطة للغاية، افترض أنه تم حفظ الملف بدءًا من 2000070. الكود كما يلي:
انسخ رمز الكود كما يلي:
RandomAccess oSavedFile = new RandomAccessFile("down.zip"،"rw")؛
طويل nPos = 2000070؛
// حدد موقع مؤشر الملف إلى موضع nPos
oSavedFile.seek(nPos);
بايت[] ب = بايت جديد[1024];
كثافة العمليات ن القراءة؛
// اقرأ دفق البايت من دفق الإدخال واكتبه في الملف
بينما((nRead=input.read(b,0,1024)) > 0)
{
oSavedFile.write(b,0,nRead);
}
على أية حال، الأمر بسيط للغاية.
والشيء التالي الذي يجب فعله هو دمجه في برنامج كامل. بما في ذلك سلسلة من التحكم في الخيط وما إلى ذلك.
(3) يستخدم تنفيذ نواة استئناف نقطة التوقف <BR> بشكل أساسي 6 فئات، بما في ذلك فئة الاختبار.
SiteFileFetch.java مسؤول عن الاستيلاء على الملف بأكمله والتحكم في الخيط الداخلي (فئة FileSplitterFetch).
FileSplitterFetch.java مسؤول عن جلب بعض الملفات.
FileAccess.java مسؤول عن تخزين الملفات.
معلومات حول الملف الذي يريد SiteInfoBean.java الزحف إليه، مثل الدليل الذي تم حفظ الملف فيه، واسمه، وعنوان URL للملف الذي تم الزحف إليه، وما إلى ذلك.
فئة الأدوات Utility.java، ضع بعض الطرق البسيطة.
فئة اختبار TestMethod.java.
وفيما يلي البرنامج المصدر:
انسخ رمز الكود كما يلي:
/*
** SiteFileFetch.java
*/
حزمة نت فوكس؛
استيراد java.io.*;
استيراد java.net.*;
الطبقة العامة SiteFileFetch تمتد الموضوع {
SiteInfoBean siteInfoBean = null; // معلومات الملف Bean
long[] nStartPos;
long[] nEndPos;
FileSplitterFetch[] fileSplitterFetch; // كائن الخيط الفرعي
long nFileLength; // طول الملف
boolean bFirst = true; // ما إذا كان سيتم جلب الملف لأول مرة
boolean bStop = false;
ملف tmpFile; // معلومات مؤقتة لتنزيل الملف
DataOutputStream Output; // دفق الإخراج إلى الملف
يطرح SiteFileFetch العام (SiteInfoBean bean) IOException
{
siteInfoBean = bean;
//tmpFile = File.createTempFile("zhong"،"1111"،new File(bean.getSFilePath()));
tmpFile = new File(bean.getSFilePath()+File.separator + bean.getSFileName()+".info");
إذا (tmpFile.exists ())
{
bFirst = false;
read_nPos();
}
آخر
{
nStartPos = new long[bean.getNSplitter()];
nEndPos = new long[bean.getNSplitter()];
}
}
تشغيل الفراغ العام ()
{
// احصل على طول الملف
// تقسيم الملف
//مثيل FileSplitterFetch
// ابدأ موضوع FileSplitterFetch
// انتظر عودة الخيط الفرعي
يحاول{
إذا (بأولاً)
{
nFileLength = getFileSize();
إذا (nFileLength == -1)
{
System.err.println("طول الملف غير معروف!");
}
وإلا إذا (nFileLength == -2)
{
System.err.println("لا يمكن الوصول إلى الملف!");
}
آخر
{
ل(int i=0;i<nStartPos.length;i++)
{
nStartPos[i] = (long)(i*(nFileLength/nStartPos.length));
}
ل(int i=0;i<nEndPos.length-1;i++)
{
nEndPos[i] = nStartPos[i+1];
}
nEndPos[nEndPos.length-1] = nFileLength;
}
}
// ابدأ موضوع الطفل
fileSplitterFetch = new FileSplitterFetch[nStartPos.length];
ل(int i=0;i<nStartPos.length;i++)
{
fileSplitterFetch[i] = new FileSplitterFetch(siteInfoBean.getSSiteURL(),
siteInfoBean.getSFilePath() + File.separator + siteInfoBean.getSFileName(),
nStartPos[i],nEndPos[i],i);
Utility.log("Thread " + i + " , nStartPos = " + nStartPos[i] + ", nEndPos = " + nEndPos[i]);
fileSplitterFetch[i].start();
}
// fileSplitterFetch[nPos. length-1] = new FileSplitterFetch(siteInfoBean.getSSiteURL(),
siteInfoBean.getSFilePath() + File.separator + siteInfoBean.getSFileName(),nPos[nPos.length-1],nFileLength,nPos.length-1);
// Utility.log("Thread " + (nPos.length-1) + "، nStartPos = " + nPos[nPos.length-1] + "،
nEndPos = "+ nFileLength);
// fileSplitterFetch[nPos. length-1].start();
// انتظر حتى ينتهي الخيط الفرعي
// عدد كثافة العمليات = 0؛
// ما إذا كان سيتم إنهاء الحلقة بينما
استراحة منطقية = خطأ؛
بينما (! بستوب)
{
write_nPos();
Utility.sleep(500);
BreakWhile = true;
ل(int i=0;i<nStartPos.length;i++)
{
إذا (!fileSplitterFetch[i].bDownOver)
{
BreakWhile = false;
استراحة؛
}
}
إذا (استراحة)
استراحة؛
//العدد++;
//إذا (العدد>4)
// siteStop();
}
System.err.println("اكتمل تنزيل الملف!");
}
قبض (استثناء ه) {e.printStackTrace ()؛}
}
// احصل على طول الملف
getFileSize () العام الطويل
{
int nFileLength = -1;
يحاول{
URL url = عنوان URL الجديد(siteInfoBean.getSSiteURL());
HttpURLConnection httpConnection = (HttpURLConnection)url.openConnection ();
httpConnection.setRequestProperty("User-Agent"،"NetFox");
int ResponseCode=httpConnection.getResponseCode();
إذا (رمز الاستجابة> = 400)
{
ProcessErrorCode(responseCode);
return -2; //-2 يمثل الوصول إلى الخطأ
}
رأس السلسلة؛
ل(int i=1;;i++)
{
//DataInputStream in = new DataInputStream(httpConnection.getInputStream());
//Utility.log(in.readLine());
sHeader=httpConnection.getHeaderFieldKey(i);
إذا (sHeader!=null)
{
إذا (sHeader.equals("طول المحتوى"))
{
nFileLength = Integer.parseInt(httpConnection.getHeaderField(sHeader));
استراحة؛
}
}
آخر
استراحة؛
}
}
الصيد (IOException e) {e.printStackTrace ()؛}
قبض (استثناء ه) {e.printStackTrace ()؛}
Utility.log(nFileLength);
إرجاع nFileLength؛
}
// حفظ معلومات التنزيل (موضع مؤشر الملف)
write_nPos () الفراغ الخاص
{
يحاول{
Output = new DataOutputStream(new FileOutputStream(tmpFile));
input.writeInt(nStartPos.length);
ل(int i=0;i<nStartPos.length;i++)
{
//output.writeLong(nPos[i]);
input.writeLong(fileSplitterFetch[i].nStartPos);
input.writeLong(fileSplitterFetch[i].nEndPos);
}
الإخراج. إغلاق ()؛
}
الصيد (IOException e) {e.printStackTrace ()؛}
قبض (استثناء ه) {e.printStackTrace ()؛}
}
// اقرأ معلومات التنزيل المحفوظة (موضع مؤشر الملف)
read_nPos () باطلة خاصة
{
يحاول{
DataInputStream input = new DataInputStream(new FileInputStream(tmpFile));
int nCount = input.readInt();
nStartPos = new long[nCount];
nEndPos = new long[nCount];
ل(int i=0;i<nStartPos.length;i++)
{
nStartPos[i] = input.readLong();
nEndPos[i] = input.readLong();
}
input. Close();
}
الصيد (IOException e) {e.printStackTrace ()؛}
قبض (استثناء ه) {e.printStackTrace ()؛}
}
رمز خطأ العملية الخاص (int nErrorCode)
{
System.err.println("رمز الخطأ:" + nErrorCode);
}
// إيقاف تنزيل الملف
موقع الفراغ العامStop()
{
bStop = true;
ل(int i=0;i<nStartPos.length;i++)
fileSplitterFetch[i].splitterStop();
}
}
/*
** FileSplitterFetch.java
*/
حزمة نت فوكس؛
استيراد java.io.*;
استيراد java.net.*;
الطبقة العامة FileSplitterFetch تمتد الموضوع {
سلسلة sURL؛ // عنوان URL للملف
long nStartPos; // موضع بدء مقتطف الملف
long nEndPos; // موضع نهاية مقتطف الملف
int nThreadID; // معرف الموضوع
boolean bDownOver = false;
boolean bStop = false;
FileAccessI fileAccessI = null; // واجهة الوصول إلى الملفات
FileSplitterFetch العام (String sURL، String sName، long nStart، long nEnd، int id) يلقي IOException
{
this.sURL = sURL;
this.nStartPos = nStart;
this.nEndPos = nEnd;
nThreadID = id;
fileAccessI = new FileAccessI(sName,nStartPos);
}
تشغيل الفراغ العام ()
{
بينما (nStartPos < nEndPos && !bStop)
{
يحاول{
عنوان URL = عنوان URL الجديد (sURL)؛
HttpURLConnection httpConnection = (HttpURLConnection)url.openConnection ();
httpConnection.setRequestProperty("User-Agent"،"NetFox");
String sProperty = "bytes="+nStartPos+"-";
httpConnection.setRequestProperty("RANGE",sProperty);
Utility.log(sProperty);
InputStream input = httpConnection.getInputStream();
//logResponseHead(httpConnection);
بايت[] ب = بايت جديد[1024];
كثافة العمليات ن القراءة؛
while((nRead=input.read(b,0,1024)) > 0 && nStartPos < nEndPos && !bStop)
{
nStartPos += fileAccessI.write(b,0,nRead);
//إذا (نثريديد == 1)
// Utility.log("nStartPos = " + nStartPos + ", nEndPos = " + nEndPos);
}
Utility.log("انتهى الموضوع " + nThreadID + "!");
bDownOver = true;
//nPos = fileAccessI.write (b,0,nRead);
}
قبض (استثناء ه) {e.printStackTrace ()؛}
}
}
// طباعة معلومات رأس الاستجابة
logResponseHead العام الفارغ (HttpURLConnection con)
{
ل(int i=1;;i++)
{
String header=con.getHeaderFieldKey(i);
إذا (الرأس!=خالية)
//responseHeaders.put(header,httpConnection.getHeaderField(header));
Utility.log(header+" : "+con.getHeaderField(header));
آخر
استراحة؛
}
}
الفاصل الفراغي العام ()
{
bStop = true;
}
}
/*
** FileAccess.java
*/
حزمة نت فوكس؛
استيراد java.io.*;
الطبقة العامة FileAccessI تنفذ Serializable {
RandomAccessFile oSavedFile;
نقاط البيع الطويلة؛
يقوم FileAccessI() العام بطرح IOException
{
هذا(""،0);
}
FileAccessI العام (String sName، long nPos) يطرح IOException
{
oSavedFile = new RandomAccessFile(sName,"rw");
this.nPos = nPos;
oSavedFile.seek(nPos);
}
كتابة int العامة المتزامنة (byte[] b،int nStart،int nLen)
{
كثافة العمليات ن = -1؛
يحاول{
oSavedFile.write(b,nStart,nLen);
n = nLen;
}
قبض على (IOException ه)
{
printStackTrace();
}
العودة ن؛
}
}
/*
**SiteInfoBean.java
*/
حزمة نت فوكس؛
الطبقة العامة SiteInfoBean {
سلسلة خاصة sSiteURL؛ // عنوان URL للموقع
سلسلة خاصة sFilePath؛ // مسار الملف المحفوظ
سلسلة خاصة sFileName؛ // اسم الملف المحفوظ
Private int nSplitter; // عدد ملفات التنزيل المقسمة
SiteInfoBean العام ()
{
// القيمة الافتراضية لـ nSplitter هي 5
هذا(""،"،"،5)؛
}
SiteInfoBean العام (String sURL، String sPath، String sName، int nSpiltter)
{
sSiteURL= sURL;
sFilePath = sPath;
sFileName = sName;
this.nSplitter = nSpiltter;
}
السلسلة العامة getSSiteURL()
{
إرجاع sSiteURL؛
}
setSSiteURL باطلة عامة (قيمة السلسلة)
{
sSiteURL = value;
}
سلسلة عامة getSFilePath ()
{
إرجاع sFilePath؛
}
مجموعة الفراغ العام (قيمة السلسلة)
{
sFilePath = value;
}
سلسلة عامة getSFileName ()
{
إرجاع sFileName؛
}
setSFileName باطلة عامة (قيمة السلسلة)
{
sFileName = value;
}
int public getNSplitter()
{
إرجاع nSplitter؛
}
مجموعة الفراغ العام NSplitter (int nCount)
{
nSplitter = nCount;
}
}
/*
** فائدة.جافا
*/
حزمة نت فوكس؛
فائدة الطبقة العامة {
المرافق العامة()
{
}
سكون الفراغ الثابت العام (int nSecond)
{
يحاول{
Thread.sleep(nSecond);
}
قبض (استثناء ه)
{
printStackTrace();
}
}
سجل الفراغ الثابت العام (سلسلة sMsg)
{
System.err.println(sMsg);
}
سجل الفراغ الثابت العام (int sMsg)
{
System.err.println(sMsg);
}
}
/*
**TestMethod.java
*/
حزمة نت فوكس؛
طريقة اختبار الطبقة العامة {
طريقة الاختبار العامة ()
{ ///xx/weblogic60b2_win.exe
يحاول{
SiteInfoBean bean = new SiteInfoBean("http://localhost/xx/weblogic60b2_win.exe"،"L://temp"،"weblogic60b2_win.exe"،5);
//SiteInfoBean bean = new SiteInfoBean("http://localhost:8080/down.zip"،L://temp"،"weblogic60b2_win.exe"،5);
SiteFileFetch fileFetch = new SiteFileFetch(bean);
fileFetch.start();
}
Catch(Exception e){e.printStackTrace ();
}
}
الفراغ العام الثابت الرئيسي (String[] args)
{
طريقة الاختبار الجديدة () ؛
}
}