歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Android應用開發之使用Socket進行大文件斷點上傳續傳

Android應用開發之使用Socket進行大文件斷點上傳續傳

日期:2017/3/1 10:27:56   编辑:Linux編程
在Android中上傳文件可以采用HTTP方式,也可以采用Socket方式,但是HTTP方式不能上傳大文件,這裡介紹一種通過Socket方式來進行斷點續傳的方式,服務端會記錄下文件的上傳進度,當某一次上傳過程意外終止後,下一次可以繼續上傳,這裡用到的其實還是J2SE裡的知識。

這個上傳程序的原理是:客戶端第一次上傳時向服務端發送“Content-Length=35;filename=WinRAR_3.90_SC.exe;sourceid=“這種格式的字符串,服務端收到後會查找該文件是否有上傳記錄,如果有就返回已經上傳的位置,否則返回新生成的sourceid以及position為0,類似”sourceid=2324838389;position=0“這樣的字符串,客戶端收到返回後的字符串後再從指定的位置開始上傳文件。

首先是服務端代碼:

SocketServer.java

  1. package com.android.socket.server;
  2. import java.io.File;
  3. import java.io.FileInputStream;
  4. import java.io.FileOutputStream;
  5. import java.io.IOException;
  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.concurrent.ExecutorService;
  17. import java.util.concurrent.Executors;
  18. import com.android.socket.utils.StreamTool;
  19. public class SocketServer {
  20. private ExecutorService executorService;// 線程池
  21. private ServerSocket ss = null;
  22. private int port;// 監聽端口
  23. private boolean quit;// 是否退出
  24. private Map<Long, FileLog> datas = new HashMap<Long, FileLog>();// 存放斷點數據,最好改為數據庫存放
  25. public SocketServer(int port) {
  26. this.port = port;
  27. // 初始化線程池
  28. executorService = Executors.newFixedThreadPool(Runtime.getRuntime()
  29. .availableProcessors() * 50);
  30. }
  31. // 啟動服務
  32. public void start() throws Exception {
  33. ss = new ServerSocket(port);
  34. while (!quit) {
  35. Socket socket = ss.accept();// www.linuxidc.com接受客戶端的請求
  36. // 為支持多用戶並發訪問,采用線程池管理每一個用戶的連接請求
  37. executorService.execute(new SocketTask(socket));// 啟動一個線程來處理請求
  38. }
  39. }
  40. // 退出
  41. public void quit() {
  42. this.quit = true;
  43. try {
  44. ss.close();
  45. } catch (IOException e) {
  46. e.printStackTrace();
  47. }
  48. }
  49. public static void main(String[] args) throws Exception {
  50. SocketServer server = new SocketServer(8787);
  51. server.start();
  52. }
  53. private class SocketTask implements Runnable {
  54. private Socket socket;
  55. public SocketTask(Socket socket) {
  56. this.socket = socket;
  57. }
  58. @Override
  59. public void run() {
  60. try {
  61. System.out.println("accepted connenction from "
  62. + socket.getInetAddress() + " @ " + socket.getPort());
  63. PushbackInputStream inStream = new PushbackInputStream(
  64. socket.getInputStream());
  65. // 得到客戶端發來的第一行協議數據:Content-Length=143253434;filename=xxx.3gp;sourceid=
  66. // 如果用戶初次上傳文件,sourceid的值為空。
  67. String head = StreamTool.readLine(inStream);
  68. System.out.println(head);
  69. if (head != null) {
  70. // 下面從協議數據中讀取各種參數值
  71. String[] items = head.split(";");
  72. String filelength = items[0].substring(items[0].indexOf("=") + 1);
  73. String filename = items[1].substring(items[1].indexOf("=") + 1);
  74. String sourceid = items[2].substring(items[2].indexOf("=") + 1);
  75. Long id = System.currentTimeMillis();
  76. FileLog log = null;
  77. if (null != sourceid && !"".equals(sourceid)) {
  78. id = Long.valueOf(sourceid);
  79. log = find(id);//查找上傳的文件是否存在上傳記錄
  80. }
  81. File file = null;
  82. int position = 0;
  83. if(log==null){//如果上傳的文件不存在上傳記錄,為文件添加跟蹤記錄
  84. String path = new SimpleDateFormat("yyyy/MM/dd/HH/mm").format(new Date());
  85. File dir = new File("file/"+ path);
  86. if(!dir.exists()) dir.mkdirs();
  87. file = new File(dir, filename);
  88. if(file.exists()){//如果上傳的文件發生重名,然後進行改名
  89. filename = filename.substring(0, filename.indexOf(".")-1)+ dir.listFiles().length+ filename.substring(filename.indexOf("."));
  90. file = new File(dir, filename);
  91. }
  92. save(id, file);
  93. }else{// 如果上傳的文件存在上傳記錄,讀取上次的斷點位置
  94. file = new File(log.getPath());//從上傳記錄中得到文件的路徑
  95. if(file.exists()){
  96. File logFile = new File(file.getParentFile(), file.getName()+".log");
  97. if(logFile.exists()){
  98. Properties properties = new Properties();
  99. properties.load(new FileInputStream(logFile));
  100. position = Integer.valueOf(properties.getProperty("length"));//讀取斷點位置
  101. }
  102. }
  103. }
  104. OutputStream outStream = socket.getOutputStream();
  105. String response = "sourceid="+ id+ ";position="+ position+ "\r\n";
  106. //服務器收到客戶端的請求信息後,給客戶端返回響應信息:sourceid=1274773833264;position=0
  107. //sourceid由服務生成,唯一標識上傳的文件,position指示客戶端從文件的什麼位置開始上傳
  108. outStream.write(response.getBytes());
  109. RandomAccessFile fileOutStream = new RandomAccessFile(file, "rwd");
  110. if(position==0) fileOutStream.setLength(Integer.valueOf(filelength));//設置文件長度
  111. fileOutStream.seek(position);//移動文件指定的位置開始寫入數據
  112. byte[] buffer = new byte[1024];
  113. int len = -1;
  114. int length = position;
  115. while( (len=inStream.read(buffer)) != -1){//從輸入流中讀取數據寫入到文件中
  116. fileOutStream.write(buffer, 0, len);
  117. length += len;
  118. Properties properties = new Properties();
  119. properties.put("length", String.valueOf(length));
  120. FileOutputStream logFile = new FileOutputStream(new File(file.getParentFile(), file.getName()+".log"));
  121. properties.store(logFile, null);//實時記錄文件的最後保存位置
  122. logFile.close();
  123. }
  124. if(length==fileOutStream.length()) delete(id);
  125. fileOutStream.close();
  126. inStream.close();
  127. outStream.close();
  128. file = null;
  129. }
  130. } catch (Exception e) {
  131. e.printStackTrace();
  132. } finally {
  133. try {
  134. if(socket != null && !socket.isClosed()) socket.close();
  135. } catch (IOException e) {}
  136. }
  137. }
  138. }
  139. public FileLog find(Long sourceid) {
  140. return datas.get(sourceid);
  141. }
  142. // 保存上傳記錄
  143. public void save(Long id, File saveFile) {
  144. // 日後可以改成通過數據庫存放
  145. datas.put(id, new FileLog(id, saveFile.getAbsolutePath()));
  146. }
  147. // 當文件上傳完畢,刪除記錄
  148. public void delete(long sourceid) {
  149. if (datas.containsKey(sourceid))
  150. datas.remove(sourceid);
  151. }
  152. private class FileLog {
  153. private Long id;
  154. private String path;
  155. public FileLog(Long id, String path) {
  156. super();
  157. this.id = id;
  158. this.path = path;
  159. }
  160. public Long getId() {
  161. return id;
  162. }
  163. public void setId(Long id) {
  164. this.id = id;
  165. }
  166. public String getPath() {
  167. return path;
  168. }
  169. public void setPath(String path) {
  170. this.path = path;
  171. }
  172. }
  173. }

ServerWindow.java

  1. package com.android.socket.server;
  2. import java.awt.BorderLayout;
  3. import java.awt.Frame;
  4. import java.awt.Label;
  5. import java.awt.event.WindowEvent;
  6. import java.awt.event.WindowListener;
  7. public class ServerWindow extends Frame{
  8. private SocketServer server;
  9. private Label label;
  10. public ServerWindow(String title){
  11. super(title);
  12. server = new SocketServer(8787);
  13. label = new Label();
  14. add(label, BorderLayout.PAGE_START);
  15. label.setText("服務器已經啟動www.linuxidc.com");
  16. this.addWindowListener(new WindowListener() {
  17. @Override
  18. public void windowOpened(WindowEvent e) {
  19. new Thread(new Runnable() {
  20. @Override
  21. public void run() {
  22. try {
  23. server.start();
  24. } catch (Exception e) {
  25. e.printStackTrace();
  26. }
  27. }
  28. }).start();
  29. }
  30. @Override
  31. public void windowIconified(WindowEvent e) {
  32. }
  33. @Override
  34. public void windowDeiconified(WindowEvent e) {
  35. }
  36. @Override
  37. public void windowDeactivated(WindowEvent e) {
  38. }
  39. @Override
  40. public void windowClosing(WindowEvent e) {
  41. server.quit();
  42. System.exit(0);
  43. }
  44. @Override
  45. public void windowClosed(WindowEvent e) {
  46. }
  47. @Override
  48. public void windowActivated(WindowEvent e) {
  49. }
  50. });
  51. }
  52. /**
  53. * @param args
  54. */
  55. public static void main(String[] args) {
  56. ServerWindow window = new ServerWindow("文件上傳服務端");
  57. window.setSize(300, 300);
  58. window.setVisible(true);
  59. }
  60. }
工具類StreamTool.java
  1. package com.android.socket.utils;
  2. import java.io.ByteArrayOutputStream;
  3. import java.io.File;
  4. import java.io.FileOutputStream;
  5. import java.io.IOException;
  6. import java.io.InputStream;
  7. import java.io.PushbackInputStream;
  8. public class StreamTool {
  9. public static void save(File file, byte[] data) throws Exception {
  10. FileOutputStream outStream = new FileOutputStream(file);
  11. outStream.write(data);
  12. outStream.close();
  13. }
  14. public static String readLine(PushbackInputStream in) throws IOException {
  15. char buf[] = new char[128];
  16. int room = buf.length;
  17. int offset = 0;
  18. int c;
  19. loop: while (true) {
  20. switch (c = in.read()) {
  21. case -1:
  22. case '\n':
  23. break loop;
  24. case '\r':
  25. int c2 = in.read();
  26. if ((c2 != '\n') && (c2 != -1)) in.unread(c2);
  27. break loop;
  28. default:
  29. if (--room < 0) {
  30. char[] lineBuffer = buf;
  31. buf = new char[offset + 128];
  32. room = buf.length - offset - 1;
  33. System.arraycopy(lineBuffer, 0, buf, 0, offset);
  34. }
  35. buf[offset++] = (char) c;
  36. break;
  37. }
  38. }
  39. if ((c == -1) && (offset == 0)) return null;
  40. return String.copyValueOf(buf, 0, offset);
  41. }
  42. /**
  43. * 讀取流
  44. * @param inStream
  45. * @return 字節數組
  46. * @throws Exception
  47. */
  48. public static byte[] readStream(InputStream inStream) throws Exception{
  49. ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
  50. byte[] buffer = new byte[1024];
  51. int len = -1;
  52. while( (len=inStream.read(buffer)) != -1){
  53. outSteam.write(buffer, 0, len);
  54. }
  55. outSteam.close();
  56. inStream.close();
  57. return outSteam.toByteArray();
  58. }
  59. }
Android客戶端代碼:

Copyright © Linux教程網 All Rights Reserved