(1) El principio de descarga reanudable <BR>De hecho, el principio de descarga reanudable es muy simple, solo que la solicitud HTTP es diferente de la descarga general.
Por ejemplo, cuando un navegador solicita un archivo en el servidor, la solicitud que realiza es la siguiente:
Supongamos que el nombre de dominio del servidor es wwww.sjtu.edu.cn y el nombre del archivo es down.zip.
OBTENER /down.zip HTTP/1.1
Aceptar: imagen/gif, imagen/x-xbitmap, imagen/jpeg, imagen/pjpeg, aplicación/vnd.ms-
excel, aplicación/msword, aplicación/vnd.ms-powerpoint, */*
Aceptar-Idioma: zh-cn
Aceptar codificación: gzip, deflate
Agente de usuario: Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)
Conexión: Mantener vivo
Una vez que el servidor recibe la solicitud, busca el archivo solicitado según sea necesario, extrae la información del archivo y luego lo devuelve al navegador. La información devuelta es la siguiente.
200
Longitud del contenido = 106786028
Rangos de aceptación = bytes
Fecha = lunes, 30 de abril de 2001 12:56:11 GMT
Etiqueta ET=W/"02ca57e173c11:95b"
Tipo de contenido = aplicación/flujo de octeto
Servidor=Microsoft-IIS/5.0
Última modificación = lunes, 30 de abril de 2001 12:56:11 GMT
La llamada reanudación de descarga significa continuar la descarga desde el punto donde se descargó el archivo. Entonces en el navegador del cliente pase
Cuando se trata de servidores web, es necesario agregar un dato más: por dónde empezar.
El siguiente es un "navegador" compilado por mí para pasar la información de la solicitud al servidor web. La solicitud comienza desde 2000070 bytes.
OBTENER /down.zip HTTP/1.0
Agente de usuario: NetFox
RANGO: bytes=2000070-
Aceptar: texto/html, imagen/gif, imagen/jpeg, *; q=.2, */*;
Si miras con atención, encontrarás una línea adicional de RANGE: bytes=2000070-
El significado de esta línea es decirle al servidor que el archivo down.zip se transmitirá a partir de 2000070 bytes y que no es necesario transmitir los bytes anteriores.
Después de que el servidor recibe esta solicitud, la información devuelta es la siguiente:
206
Longitud del contenido = 106786028
Rango de contenido = bytes 2000070-106786027/106786028
Fecha = lunes, 30 de abril de 2001 12:55:20 GMT
Etiqueta ET=W/"02ca57e173c11:95b"
Tipo de contenido = aplicación/flujo de octeto
Servidor=Microsoft-IIS/5.0
Última modificación = lunes, 30 de abril de 2001 12:55:20 GMT
Comparándolo con la información devuelta por el servidor anterior, encontrará que se ha agregado una línea:
Rango de contenido = bytes 2000070-106786027/106786028
El código de retorno también se cambió a 206 en lugar de 200.
Conociendo los principios anteriores, puede programar la descarga de reanudación del punto de interrupción.
(2) Puntos clave en la implementación de la transferencia de currículum de punto de interrupción en Java
(1) Qué método se utiliza para enviar RANGE: bytes=2000070-.
Por supuesto, definitivamente se puede hacer usando el Socket original, pero eso sería demasiado problemático. De hecho, el paquete net de Java proporciona esta función. El código es el siguiente:
URL URL = nueva URL ("http://www.sjtu.edu.cn/down.zip");
HttpURLConnection httpConnection = (HttpURLConnection)url.openConnection();
//Establecer usuario-agente
httpConnection.setRequestProperty("Agente de usuario","NetFox");
// Establece la posición inicial de la transmisión de reanudación del punto de interrupción
httpConnection.setRequestProperty("RANGE","bytes=2000070");
//Obtener flujo de entrada
Entrada de flujo de entrada = httpConnection.getInputStream();
El flujo de bytes extraído del flujo de entrada es el flujo de bytes que comienza en 2000070 en el archivo down.zip.
Verá, en realidad es muy sencillo implementar la transferencia de currículum de punto de interrupción en Java.
Lo siguiente que debe hacer es cómo guardar la secuencia obtenida en un archivo.
El método utilizado para guardar el archivo.
Utilizo la clase RandAccessFile en el paquete IO.
La operación es bastante simple. Supongamos que el archivo se guarda a partir de 2000070. El código es el siguiente:
Copie el código de código de la siguiente manera:
RandomAccess oSavedFile = new RandomAccessFile("down.zip","rw");
nPos largo = 2000070;
// Localiza el puntero del archivo en la posición nPos
oSavedFile.seek(nPos);
byte[] b = nuevo byte[1024];
int nLeer;
//Lee el flujo de bytes del flujo de entrada y lo escribe en el archivo
mientras((nRead=entrada.read(b,0,1024)) > 0)
{
oSavedFile.write(b,0,nRead);
}
De todos modos, es muy sencillo.
Lo siguiente que debemos hacer es integrarlo en un programa completo. Incluyendo una serie de control de hilos, etc.
(3) La implementación del kernel de reanudación del punto de interrupción <BR> utiliza principalmente 6 clases, incluida una clase de prueba.
SiteFileFetch.java es responsable de capturar el archivo completo y controlar el hilo interno (clase FileSplitterFetch).
FileSplitterFetch.java es responsable de recuperar algunos archivos.
FileAccess.java es responsable del almacenamiento de archivos.
Información sobre el archivo que SiteInfoBean.java desea rastrear, como el directorio donde se guarda el archivo, su nombre, la URL del archivo rastreado, etc.
Clase de herramienta Utility.java, incluye algunos métodos simples.
Clase de prueba TestMethod.java.
El siguiente es el programa fuente:
Copie el código de código de la siguiente manera:
/*
**SiteFileFetch.java
*/
paquete NetFox;
importar java.io.*;
importar java.net.*;
clase pública SiteFileFetch extiende Thread {
SiteInfoBean siteInfoBean = null //Información del archivo Bean;
long[] nStartPos; //Posición inicial
long[] nEndPos; //Posición final
FileSplitterFetch[] fileSplitterFetch //Objeto subproceso
long nFileLength //Longitud del archivo
boolean bFirst = true; //Si buscar el archivo por primera vez
booleano bStop = false; //Señal de pare
Archivo tmpFile; //Información temporal para la descarga del archivo
Salida de DataOutputStream; // Flujo de salida al archivo
SiteFileFetch público (bean SiteInfoBean) lanza IOException
{
siteInfoBean = frijol;
//tmpFile = File.createTempFile ("zhong","1111",new File(bean.getSFilePath()));
tmpFile = nuevo Archivo(bean.getSFilePath()+File.separator + bean.getSFileName()+".info");
si(tmpFile.exists ())
{
bPrimero = falso;
leer_nPos();
}
demás
{
nStartPos = nuevo largo[bean.getNSplitter()];
nEndPos = nuevo largo[bean.getNSplitter()];
}
}
ejecución pública vacía()
{
//Obtener la longitud del archivo
//dividir archivo
// Instancia FileSplitterFetch
//Iniciamos el hilo FileSplitterFetch
//Esperar a que regrese el hilo secundario
intentar{
si(bprimero)
{
nFileLength = getFileSize();
si(nFileLength == -1)
{
System.err.println("¡Se desconoce la longitud del archivo!");
}
de lo contrario si (nFileLength == -2)
{
System.err.println("¡No se puede acceder al archivo!");
}
demás
{
para(int i=0;i<nStartPos.length;i++)
{
nStartPos[i] = (largo)(i*(nFileLength/nStartPos.length));
}
para(int i=0;i<nEndPos.length-1;i++)
{
nPosFin[i] = nPosInicio[i+1];
}
nEndPos[nEndPos.length-1] = nFileLength;
}
}
//Iniciar hilo secundario
fileSplitterFetch = nuevo FileSplitterFetch[nStartPos.length];
para(int i=0;i<nStartPos.length;i++)
{
fileSplitterFetch[i] = nuevo FileSplitterFetch(siteInfoBean.getSSiteURL(),
siteInfoBean.getSFilePath() + Archivo.separador + siteInfoBean.getSFileName(),
nPosInicio[i],nPosEnd[i],i);
Utility.log("Subproceso " + i + " , nStartPos = " + nStartPos[i] + ", nEndPos = " + nEndPos[i]);
fileSplitterFetch[i].start();
}
// fileSplitterFetch[nPos.length-1] = nuevo FileSplitterFetch(siteInfoBean.getSSiteURL(),
siteInfoBean.getSFilePath() + Archivo.separador + siteInfoBean.getSFileName(),nPos[nPos.length-1],nFileLength,nPos.length-1);
// Utility.log("Subproceso " + (nPos.length-1) + " , nStartPos = " + nPos[nPos.length-1] + ",
nEndPos = " + nFileLength);
// fileSplitterFetch[nPos.length-1].start();
//Esperar a que finalice el hilo secundario
//int recuento = 0;
//Si finalizar el ciclo while
pausa booleanaMientras = falso;
mientras(!bDetener)
{
escribir_nPos();
Utilidad.dormir(500);
descansoMientras = verdadero;
para(int i=0;i<nStartPos.length;i++)
{
si(!fileSplitterFetch[i].bDownOver)
{
descansoMientras = falso;
romper;
}
}
si(descansoMientras)
romper;
//cuenta++;
//si(cuenta>4)
// parada del sitio();
}
System.err.println("¡Descarga del archivo completada!");
}
captura (Excepción e) {e.printStackTrace ();}
}
//Obtener la longitud del archivo
público largo getFileSize()
{
int nFileLength = -1;
intentar{
URL URL = nueva URL (siteInfoBean.getSSiteURL());
HttpURLConnection httpConnection = (HttpURLConnection)url.openConnection ();
httpConnection.setRequestProperty("Agente de usuario","NetFox");
int código de respuesta = httpConnection.getResponseCode();
si(código de respuesta>=400)
{
código de error de proceso (código de respuesta);
return -2; //-2 representa el acceso es un error
}
Encabezado de cadena;
para(int i=1;;i++)
{
//DataInputStream en = new DataInputStream(httpConnection.getInputStream ());
//Utility.log(in.readLine());
sHeader=httpConnection.getHeaderFieldKey(i);
si (encabezado! = nulo)
{
if(sHeader.equals("Contenido-Longitud"))
{
nFileLength = Integer.parseInt(httpConnection.getHeaderField(sHeader));
romper;
}
}
demás
romper;
}
}
captura(IOException e){e.printStackTrace ();}
captura (Excepción e) {e.printStackTrace ();}
Utility.log(nFileLength);
devolver nFileLength;
}
//Guardar información de descarga (posición del puntero del archivo)
escritura nula privada_nPos()
{
intentar{
salida = nuevo DataOutputStream(nuevo FileOutputStream(tmpFile));
salida.writeInt(nStartPos.length);
para(int i=0;i<nStartPos.length;i++)
{
// salida.writeLong(nPos[i]);
salida.writeLong(fileSplitterFetch[i].nStartPos);
salida.writeLong(fileSplitterFetch[i].nEndPos);
}
salida.close();
}
captura(IOException e){e.printStackTrace ();}
captura (Excepción e) {e.printStackTrace ();}
}
//Lee la información de descarga guardada (posición del puntero del archivo)
vacío privado read_nPos()
{
intentar{
Entrada DataInputStream = nuevo DataInputStream (nuevo FileInputStream (tmpFile));
int nCount = entrada.readInt();
nStartPos = nuevo largo[nCount];
nEndPos = nuevo largo[nCount];
para(int i=0;i<nStartPos.length;i++)
{
nStartPos[i] = input.readLong();
nEndPos[i] = input.readLong();
}
entrada.cerrar();
}
captura(IOException e){e.printStackTrace ();}
captura (Excepción e) {e.printStackTrace ();}
}
proceso vacío privadoErrorCode(int nErrorCode)
{
System.err.println("Código de error: " + nErrorCode);
}
//Detener descarga de archivo
sitio público vacíoStop()
{
bParar = verdadero;
para(int i=0;i<nStartPos.length;i++)
fileSplitterFetch[i].splitterStop();
}
}
/*
**FileSplitterFetch.java
*/
paquete NetFox;
importar java.io.*;
importar java.net.*;
clase pública FileSplitterFetch extiende Thread {
Cadena sURL; //URL del archivo
long nStartPos //Posición de inicio del fragmento de archivo
long nEndPos; //Posición final del fragmento de archivo
int nThreadID; //ID del hilo
boolean bDownOver = false; //El descenso ha terminado.
boolean bStop = false; //Detener idéntico
FileAccessI fileAccessI = null //Interfaz de acceso a archivos
public FileSplitterFetch(String sURL,String sName,long nStart,long nEnd,int id) lanza IOException
{
this.URL = URL;
this.nStartPos = nInicio;
this.nEndPos = nEnd;
nThreadID = identificación;
fileAccessI = new FileAccessI(sNombre,nPosInicio);
}
ejecución pública vacía()
{
mientras(nPosInicio < nPosFin && !bParar)
{
intentar{
URL URL = nueva URL (sURL);
HttpURLConnection httpConnection = (HttpURLConnection)url.openConnection ();
httpConnection.setRequestProperty("Agente de usuario","NetFox");
Cadena sProperty = "bytes="+nStartPos+"-";
httpConnection.setRequestProperty("RANGE",sProperty);
Utility.log(sProperty);
Entrada de flujo de entrada = httpConnection.getInputStream();
//logResponseHead(httpConnection);
byte[] b = nuevo byte[1024];
int nLeer;
while((nRead=input.read(b,0,1024)) > 0 && nStartPos < nEndPos &&!bStop)
{
nStartPos += fileAccessI.write(b,0,nRead);
//si(nThreadID == 1)
// Utility.log("nStartPos = " + nStartPos + ", nEndPos = " + nEndPos);
}
Utility.log("¡El hilo " + nThreadID + " ha terminado!");
bDownOver = verdadero;
//nPos = fileAccessI.write (b,0,nRead);
}
captura (Excepción e) {e.printStackTrace ();}
}
}
//Imprimir información del encabezado de respuesta
logResponseHead público vacío (estafa HttpURLConnection)
{
para(int i=1;;i++)
{
Encabezado de cadena=con.getHeaderFieldKey(i);
si (encabezado! = nulo)
//responseHeaders.put(encabezado,httpConnection.getHeaderField(encabezado));
Utility.log(encabezado+" : "+con.getHeaderField(encabezado));
demás
romper;
}
}
divisor de vacío públicoStop()
{
bParar = verdadero;
}
}
/*
**Acceso a archivos.java
*/
paquete NetFox;
importar java.io.*;
la clase pública FileAccessI implementa Serializable{
Archivo de acceso aleatorio o Archivo guardado;
nPos largos;
FileAccessI público () lanza IOException
{
esto("",0);
}
public FileAccessI (String sName, long nPos) lanza IOException
{
oSavedFile = new RandomAccessFile(sName,"rw");
this.nPos = nPos;
oSavedFile.seek(nPos);
}
escritura int sincronizada pública (byte [] b, int nStart, int nLen)
{
int norte = -1;
intentar{
oSavedFile.write(b,nInicio,nLen);
n = nLen;
}
captura (IOException e)
{
e.printStackTrace();
}
devolver n;
}
}
/*
**SiteInfoBean.java
*/
paquete NetFox;
clase pública SiteInfoBean {
cadena privada sSiteURL // URL del sitio
cadena privada sFilePath //Ruta del archivo guardado
cadena privada sFileName //Nombre del archivo guardado
private int nSplitter //Recuento de archivos de descarga divididos
SiteInfoBean público()
{
//el valor predeterminado de nSplitter es 5
esto("","","",5);
}
SiteInfoBean público (String sURL, String sPath, String sName, int nSpiltter)
{
sSiteURL= sURL;
sFilePath = sPath;
sNombreArchivo = sNombre;
this.nSplitter = nSpiltter;
}
cadena pública getSSiteURL()
{
devolver URL del sitio;
}
setSSiteURL público vacío (valor de cadena)
{
sSiteURL = valor;
}
cadena pública getSFilePath()
{
devolver sFilePath;
}
setSFilePath público vacío (valor de cadena)
{
sFilePath = valor;
}
cadena pública getSFileName()
{
devolver sFileName;
}
setSFileName público vacío (valor de cadena)
{
sFileName = valor;
}
público int getNSplitter()
{
devolver nDivisor;
}
conjunto vacío público NSplitter (int nCount)
{
nDivisor = nContar;
}
}
/*
**Utilidad.java
*/
paquete NetFox;
utilidad de clase pública {
utilidad pública()
{
}
sueño vacío estático público (int nSecond)
{
intentar{
Thread.sleep(nSegundo);
}
captura (Excepción e)
{
e.printStackTrace();
}
}
registro público de anulación estática (string sMsg)
{
System.err.println(sMsg);
}
registro público de anulación estática (int sMsg)
{
System.err.println(sMsg);
}
}
/*
**Métododeprueba.java
*/
paquete NetFox;
método de prueba de clase pública {
Método de prueba público()
{ ///xx/weblogic60b2_win.exe
intentar{
frijol SiteInfoBean = nuevo 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 = nuevo SiteFileFetch(bean);
fileFetch.start();
}
catch(Excepción e){e.printStackTrace ();
}
}
principal vacío estático público (String [] argumentos)
{
nuevo método de prueba();
}
}