- 論壇徽章:
- 0
|
斷點(diǎn)續(xù)傳和下載原理分析
最近做一個(gè)文件上傳和下載的應(yīng)用對文件上傳和下載進(jìn)行了一個(gè)完整的流程分析
以來是方便自己對文件上傳和下載的理解,而來便于團(tuán)隊(duì)內(nèi)部的分享
故而做了幾張圖,將整體的流程都畫下來,便于大家的理解和分析,如果有不完善的地方希望
大家多提意見,
由于參考了網(wǎng)上許多的資料,特此感謝
首先是文件上傳,這個(gè)要用到服務(wù)器
1.jpg (40.37 KB, 下載次數(shù): 3)
下載附件
2011-12-07 16:33 上傳
關(guān)鍵代碼:
FileServer.java
Java代碼- 1.import java.io.File;
- 2.import java.io.FileInputStream;
- 3.import java.io.FileOutputStream;
- 4.import java.io.IOException;
- 5.import java.io.InputStream;
- 6.import java.io.OutputStream;
- 7.import java.io.PushbackInputStream;
- 8.import java.io.RandomAccessFile;
- 9.import java.net.ServerSocket;
- 10.import java.net.Socket;
- 11.import java.text.SimpleDateFormat;
- 12.import java.util.Date;
- 13.import java.util.HashMap;
- 14.import java.util.Map;
- 15.import java.util.Properties;
- 16.import java.util.Set;
- 17.import java.util.concurrent.ExecutorService;
- 18.import java.util.concurrent.Executors;
- 19.
- 20.import util.FileLogInfo;
- 21.import util.StreamTool;
- 22.
- 23.
- 24.
- 25.public class FileServer {
- 26. private ExecutorService executorService;//線程池
- 27. private int port;//監(jiān)聽端口
- 28. private boolean quit = false;//退出
- 29. private ServerSocket server;
- 30. private Map<Long, FileLogInfo> datas = new HashMap<Long, FileLogInfo>();//存放斷點(diǎn)數(shù)據(jù),以后改為數(shù)據(jù)庫存放
- 31. public FileServer(int port)
- 32. {
- 33. this.port = port;
- 34. //創(chuàng)建線程池,池中具有(cpu個(gè)數(shù)*50)條線程
- 35. executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 50);
- 36. }
- 37.
- 38. /**
- 39. * 退出
- 40. */
- 41. public void quit()
- 42. {
- 43. this.quit = true;
- 44. try
- 45. {
- 46. server.close();
- 47. }catch (IOException e)
- 48. {
- 49. e.printStackTrace();
- 50. }
- 51. }
- 52.
- 53. /**
- 54. * 啟動服務(wù)
- 55. * @throws Exception
- 56. */
- 57. public void start() throws Exception
- 58. {
- 59. server = new ServerSocket(port);//實(shí)現(xiàn)端口監(jiān)聽
- 60. while(!quit)
- 61. {
- 62. try
- 63. {
- 64. Socket socket = server.accept();
- 65. executorService.execute(new SocketTask(socket));//為支持多用戶并發(fā)訪問,采用線程池管理每一個(gè)用戶的連接請求
- 66. }catch (Exception e)
- 67. {
- 68. e.printStackTrace();
- 69. }
- 70. }
- 71. }
- 72.
- 73. private final class SocketTask implements Runnable
- 74. {
- 75. private Socket socket = null;
- 76. public SocketTask(Socket socket)
- 77. {
- 78. this.socket = socket;
- 79. }
- 80. @Override
- 81. public void run()
- 82. {
- 83. try
- 84. {
- 85. System.out.println("FileServer accepted connection "+ socket.getInetAddress()+ ":"+ socket.getPort());
- 86. //得到客戶端發(fā)來的第一行協(xié)議數(shù)據(jù):Content-Length=143253434;filename=xxx.3gp;sourceid=
- 87. //如果用戶初次上傳文件,sourceid的值為空。
- 88. InputStream inStream = socket.getInputStream();
- 89. String head = StreamTool.readLine(inStream);
- 90. System.out.println("FileServer head:"+head);
- 91. if(head!=null)
- 92. {
- 93. //下面從協(xié)議數(shù)據(jù)中提取各項(xiàng)參數(shù)值
- 94. String[] items = head.split(";");
- 95. String filelength = items[0].substring(items[0].indexOf("=")+1);
- 96. String filename = items[1].substring(items[1].indexOf("=")+1);
- 97. String sourceid = items[2].substring(items[2].indexOf("=")+1);
- 98. //生成資源id,如果需要唯一性,可以采用UUID
- 99. long id = System.currentTimeMillis();
- 100. FileLogInfo log = null;
- 101. if(sourceid!=null && !"".equals(sourceid))
- 102. {
- 103. id = Long.valueOf(sourceid);
- 104. //查找上傳的文件是否存在上傳記錄
- 105. log = find(id);
- 106. }
- 107. File file = null;
- 108. int position = 0;
- 109. //如果上傳的文件不存在上傳記錄,為文件添加跟蹤記錄
- 110. if(log==null)
- 111. {
- 112. //設(shè)置存放的位置與當(dāng)前應(yīng)用的位置有關(guān)
- 113. File dir = new File("c:/temp/");
- 114. if(!dir.exists()) dir.mkdirs();
- 115. file = new File(dir, filename);
- 116. //如果上傳的文件發(fā)生重名,然后進(jìn)行改名
- 117. if(file.exists())
- 118. {
- 119. filename = filename.substring(0, filename.indexOf(".")-1)+ dir.listFiles().length+ filename.substring(filename.indexOf("."));
- 120. file = new File(dir, filename);
- 121. }
- 122. save(id, file);
- 123. }
- 124. // 如果上傳的文件存在上傳記錄,讀取上次的斷點(diǎn)位置
- 125. else
- 126. {
- 127. System.out.println("FileServer have exits log not null");
- 128. //從上傳記錄中得到文件的路徑
- 129. file = new File(log.getPath());
- 130. if(file.exists())
- 131. {
- 132. File logFile = new File(file.getParentFile(), file.getName()+".log");
- 133. if(logFile.exists())
- 134. {
- 135. Properties properties = new Properties();
- 136. properties.load(new FileInputStream(logFile));
- 137. //讀取斷點(diǎn)位置
- 138. position = Integer.valueOf(properties.getProperty("length"));
- 139. }
- 140. }
- 141. }
- 142. //***************************上面是對協(xié)議頭的處理,下面正式接收數(shù)據(jù)***************************************
- 143. //向客戶端請求傳輸數(shù)據(jù)
- 144. OutputStream outStream = socket.getOutputStream();
- 145. String response = "sourceid="+ id+ ";position="+ position+ "%";
- 146. //服務(wù)器收到客戶端的請求信息后,給客戶端返回響應(yīng)信息:sourceid=1274773833264;position=position
- 147. //sourceid由服務(wù)生成,唯一標(biāo)識上傳的文件,position指示客戶端從文件的什么位置開始上傳
- 148. outStream.write(response.getBytes());
- 149. RandomAccessFile fileOutStream = new RandomAccessFile(file, "rwd");
- 150. //設(shè)置文件長度
- 151. if(position==0) fileOutStream.setLength(Integer.valueOf(filelength));
- 152. //移動文件指定的位置開始寫入數(shù)據(jù)
- 153. fileOutStream.seek(position);
- 154. byte[] buffer = new byte[1024];
- 155. int len = -1;
- 156. int length = position;
- 157. //從輸入流中讀取數(shù)據(jù)寫入到文件中,并將已經(jīng)傳入的文件長度寫入配置文件,實(shí)時(shí)記錄文件的最后保存位置
- 158. while( (len=inStream.read(buffer)) != -1)
- 159. {
- 160. fileOutStream.write(buffer, 0, len);
- 161. length += len;
- 162. Properties properties = new Properties();
- 163. properties.put("length", String.valueOf(length));
- 164. FileOutputStream logFile = new FileOutputStream(new File(file.getParentFile(), file.getName()+".log"));
- 165. //實(shí)時(shí)記錄文件的最后保存位置
- 166. properties.store(logFile, null);
- 167. logFile.close();
- 168. }
- 169. //如果長傳長度等于實(shí)際長度則表示長傳成功
- 170. if(length==fileOutStream.length()){
- 171. delete(id);
- 172. }
- 173. fileOutStream.close();
- 174. inStream.close();
- 175. outStream.close();
- 176. file = null;
- 177. }
- 178. }
- 179. catch (Exception e)
- 180. {
- 181. e.printStackTrace();
- 182. }
- 183. finally{
- 184. try
- 185. {
- 186. if(socket!=null && !socket.isClosed()) socket.close();
- 187. }
- 188. catch (IOException e)
- 189. {
- 190. e.printStackTrace();
- 191. }
- 192. }
- 193. }
- 194. }
- 195.
- 196. /**
- 197. * 查找在記錄中是否有sourceid的文件
- 198. * @param sourceid
- 199. * @return
- 200. */
- 201. public FileLogInfo find(Long sourceid)
- 202. {
- 203. return datas.get(sourceid);
- 204. }
- 205.
- 206. /**
- 207. * 保存上傳記錄,日后可以改成通過數(shù)據(jù)庫存放
- 208. * @param id
- 209. * @param saveFile
- 210. */
- 211. public void save(Long id, File saveFile)
- 212. {
- 213. System.out.println("save logfile "+id);
- 214. datas.put(id, new FileLogInfo(id, saveFile.getAbsolutePath()));
- 215. }
- 216.
- 217. /**
- 218. * 當(dāng)文件上傳完畢,刪除記錄
- 219. * @param sourceid
- 220. */
- 221. public void delete(long sourceid)
- 222. {
- 223. System.out.println("delete logfile "+sourceid);
- 224. if(datas.containsKey(sourceid)) datas.remove(sourceid);
- 225. }
- 226.
- 227.}
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.OutputStream;
- import java.io.PushbackInputStream;
- import java.io.RandomAccessFile;
- import java.net.ServerSocket;
- import java.net.Socket;
- import java.text.SimpleDateFormat;
- import java.util.Date;
- import java.util.HashMap;
- import java.util.Map;
- import java.util.Properties;
- import java.util.Set;
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.Executors;
- import util.FileLogInfo;
- import util.StreamTool;
- public class FileServer {
- private ExecutorService executorService;//線程池
- private int port;//監(jiān)聽端口
- private boolean quit = false;//退出
- private ServerSocket server;
- private Map<Long, FileLogInfo> datas = new HashMap<Long, FileLogInfo>();//存放斷點(diǎn)數(shù)據(jù),以后改為數(shù)據(jù)庫存放
- public FileServer(int port)
- {
- this.port = port;
- //創(chuàng)建線程池,池中具有(cpu個(gè)數(shù)*50)條線程
- executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 50);
- }
-
- /**
- * 退出
- */
- public void quit()
- {
- this.quit = true;
- try
- {
- server.close();
- }catch (IOException e)
- {
- e.printStackTrace();
- }
- }
-
- /**
- * 啟動服務(wù)
- * @throws Exception
- */
- public void start() throws Exception
- {
- server = new ServerSocket(port);//實(shí)現(xiàn)端口監(jiān)聽
- while(!quit)
- {
- try
- {
- Socket socket = server.accept();
- executorService.execute(new SocketTask(socket));//為支持多用戶并發(fā)訪問,采用線程池管理每一個(gè)用戶的連接請求
- }catch (Exception e)
- {
- e.printStackTrace();
- }
- }
- }
-
- private final class SocketTask implements Runnable
- {
- private Socket socket = null;
- public SocketTask(Socket socket)
- {
- this.socket = socket;
- }
- @Override
- public void run()
- {
- try
- {
- System.out.println("FileServer accepted connection "+ socket.getInetAddress()+ ":"+ socket.getPort());
- //得到客戶端發(fā)來的第一行協(xié)議數(shù)據(jù):Content-Length=143253434;filename=xxx.3gp;sourceid=
- //如果用戶初次上傳文件,sourceid的值為空。
- InputStream inStream = socket.getInputStream();
- String head = StreamTool.readLine(inStream);
- System.out.println("FileServer head:"+head);
- if(head!=null)
- {
- //下面從協(xié)議數(shù)據(jù)中提取各項(xiàng)參數(shù)值
- String[] items = head.split(";");
- String filelength = items[0].substring(items[0].indexOf("=")+1);
- String filename = items[1].substring(items[1].indexOf("=")+1);
- String sourceid = items[2].substring(items[2].indexOf("=")+1);
- //生成資源id,如果需要唯一性,可以采用UUID
- long id = System.currentTimeMillis();
- FileLogInfo log = null;
- if(sourceid!=null && !"".equals(sourceid))
- {
- id = Long.valueOf(sourceid);
- //查找上傳的文件是否存在上傳記錄
- log = find(id);
- }
- File file = null;
- int position = 0;
- //如果上傳的文件不存在上傳記錄,為文件添加跟蹤記錄
- if(log==null)
- {
- //設(shè)置存放的位置與當(dāng)前應(yīng)用的位置有關(guān)
- File dir = new File("c:/temp/");
- if(!dir.exists()) dir.mkdirs();
- file = new File(dir, filename);
- //如果上傳的文件發(fā)生重名,然后進(jìn)行改名
- if(file.exists())
- {
- filename = filename.substring(0, filename.indexOf(".")-1)+ dir.listFiles().length+ filename.substring(filename.indexOf("."));
- file = new File(dir, filename);
- }
- save(id, file);
- }
- // 如果上傳的文件存在上傳記錄,讀取上次的斷點(diǎn)位置
- else
- {
- System.out.println("FileServer have exits log not null");
- //從上傳記錄中得到文件的路徑
- file = new File(log.getPath());
- if(file.exists())
- {
- File logFile = new File(file.getParentFile(), file.getName()+".log");
- if(logFile.exists())
- {
- Properties properties = new Properties();
- properties.load(new FileInputStream(logFile));
- //讀取斷點(diǎn)位置
- position = Integer.valueOf(properties.getProperty("length"));
- }
- }
- }
- //***************************上面是對協(xié)議頭的處理,下面正式接收數(shù)據(jù)***************************************
- //向客戶端請求傳輸數(shù)據(jù)
- OutputStream outStream = socket.getOutputStream();
- String response = "sourceid="+ id+ ";position="+ position+ "%";
- //服務(wù)器收到客戶端的請求信息后,給客戶端返回響應(yīng)信息:sourceid=1274773833264;position=position
- //sourceid由服務(wù)生成,唯一標(biāo)識上傳的文件,position指示客戶端從文件的什么位置開始上傳
- outStream.write(response.getBytes());
- RandomAccessFile fileOutStream = new RandomAccessFile(file, "rwd");
- //設(shè)置文件長度
- if(position==0) fileOutStream.setLength(Integer.valueOf(filelength));
- //移動文件指定的位置開始寫入數(shù)據(jù)
- fileOutStream.seek(position);
- byte[] buffer = new byte[1024];
- int len = -1;
- int length = position;
- //從輸入流中讀取數(shù)據(jù)寫入到文件中,并將已經(jīng)傳入的文件長度寫入配置文件,實(shí)時(shí)記錄文件的最后保存位置
- while( (len=inStream.read(buffer)) != -1)
- {
- fileOutStream.write(buffer, 0, len);
- length += len;
- Properties properties = new Properties();
- properties.put("length", String.valueOf(length));
- FileOutputStream logFile = new FileOutputStream(new File(file.getParentFile(), file.getName()+".log"));
- //實(shí)時(shí)記錄文件的最后保存位置
- properties.store(logFile, null);
- logFile.close();
- }
- //如果長傳長度等于實(shí)際長度則表示長傳成功
- if(length==fileOutStream.length()){
- delete(id);
- }
- fileOutStream.close();
- inStream.close();
- outStream.close();
- file = null;
- }
- }
- catch (Exception e)
- {
- e.printStackTrace();
- }
- finally{
- try
- {
- if(socket!=null && !socket.isClosed()) socket.close();
- }
- catch (IOException e)
- {
- e.printStackTrace();
- }
- }
- }
- }
-
- /**
- * 查找在記錄中是否有sourceid的文件
- * @param sourceid
- * @return
- */
- public FileLogInfo find(Long sourceid)
- {
- return datas.get(sourceid);
- }
-
- /**
- * 保存上傳記錄,日后可以改成通過數(shù)據(jù)庫存放
- * @param id
- * @param saveFile
- */
- public void save(Long id, File saveFile)
- {
- System.out.println("save logfile "+id);
- datas.put(id, new FileLogInfo(id, saveFile.getAbsolutePath()));
- }
-
- /**
- * 當(dāng)文件上傳完畢,刪除記錄
- * @param sourceid
- */
- public void delete(long sourceid)
- {
- System.out.println("delete logfile "+sourceid);
- if(datas.containsKey(sourceid)) datas.remove(sourceid);
- }
-
- }
復(fù)制代碼 由于在上面的流程圖中已經(jīng)進(jìn)行了詳細(xì)的分析,我在這兒就不講了,只是在存儲數(shù)據(jù)的時(shí)候服務(wù)器沒有用數(shù)據(jù)庫去存儲,這兒只是為了方便,所以要想測試斷點(diǎn)上傳,服務(wù)器是不能停的,否則數(shù)據(jù)就沒有了,在以后改進(jìn)的時(shí)候應(yīng)該用數(shù)據(jù)庫去存儲數(shù)據(jù)。
文件上傳客戶端:
2.jpg (38.92 KB, 下載次數(shù): 2)
下載附件
2011-12-07 16:33 上傳
|
-
3.jpg
(34.38 KB, 下載次數(shù): 3)
下載附件
2011-12-07 16:34 上傳
-
1.jpg
(40.37 KB, 下載次數(shù): 2)
下載附件
2011-12-07 16:33 上傳
-
2.jpg
(38.92 KB, 下載次數(shù): 3)
下載附件
2011-12-07 16:33 上傳
-
3.jpg
(34.38 KB, 下載次數(shù): 3)
下載附件
2011-12-07 16:34 上傳
|