Flex + Servlet 实现断点上传

公司要求实现一个大文件上传的功能,使用 JSP + SERVLET 。

如果只是上传大文件,使用cos是很好实现的。cos的上传原理是边读边写,开发人员可以自己分配缓存的大小。所以不会造成文件太大导致系统崩溃的后果。

但是后来要求实现断点续传的功能,我尝试了使用HTTP协议,不行。后来查找资料,发现许多人使用ftp协议。如果要使用ftp,又需要在客户端安装applet,据说applet太麻烦也不太好用,所以pass。

最后找到一个使用FlashPlayer10实现断点上传的例子,发现是可行的。

注:以下所包含的代码绝非原创,如有雷同绝对正常。

FlashPalyer10 以前的版本也可以上传文件,但大小却被限制在了100M以内。而10和以后的版本能够上传大文件。最大能到多少我没有试过,我试过的最大值为1G。

如果不熟悉FlashPlayer编程的请google Flex ActionScript3。

我以前也一直以为FlashPlayer就是用来看Flash的播放器,类似于暴风影音迅雷看看。

断点上传主要使用的是AS3的Socket类完成和服务器的通讯。服务器会监听两个Socket端口,一个端口是843,用来完成FlashPlayer的安全策略认证;另一个端口是用户自定义,用来完成文件上传。

安全策略认证如下:

1、客户端通过843端口向服务器发送以下内容。

 

  
  
  
  
  1. <policy-file-request/>  

2、服务器必须向客户端返回以下内容,说明验证成功。

 

  
  
  
  
  1. <?xml version=\"1.0\"?> 
  2. <cross-domain-policy> 
  3. <site-control permitted-cross-domain-policies="all"/> 
  4. <allow-access-from domain="*" to-ports="2424"/> 
  5. </cross-domain-policy>\0 

注意两个地方,to-ports表示客户端通过该端口向服务器发送接收数据。另外,结尾处的\0必须添加。

安全策略认证完成后,客户端才真正发起与目标端口的连接。

客户端的Socket监听两个事件:Event.CONNECT,ProgressEvent.SOCKET_DATA

这两个事件时AS定义的常量。Event.CONNECT在Scoeket连接时触发,可以在这里向服务发送文件名和文件大小的基本信息。ProgressEvent.SOCKET_DATA在Socket接收数据时触发,我们可以在这里向服务器传送文件的具体内容。

服务器得到上传的文件名后,在对应位置找是否已存在该文件,如果存在则得到文件大小并告诉客户端,客户端通过该信息决定从什么位置开始上传文件。如果不存在则创建文件并从头开始上传。

客户端每次上传一定大小的数据到服务器(大小自定义),如此循环直到上传完成。

下面上代码:

Upload.as

  
  
  
  
  1. package com 
  2.     import flash.events.Event; 
  3.     import flash.events.ProgressEvent; 
  4.     import flash.net.FileReference; 
  5.     import flash.net.Socket; 
  6.      
  7.     import mx.controls.Alert; 
  8.     import mx.controls.ProgressBar; 
  9.      
  10.     public class Upload 
  11.     { 
  12.          
  13.         private var socket:Socket ;      
  14.         private var fileRef:FileReference ; 
  15.         private var progressBar:ProgressBar; 
  16.         private var status:int = 0;//上传成功状态 
  17.         private var successMethod:Function; 
  18.          
  19.         public function getStatus():int
  20.             return status; 
  21.         } 
  22.          
  23.         public function setStatus(){ 
  24.             status = 1; 
  25.             successMethod.call(); 
  26.         } 
  27.          
  28.          
  29.         public function Upload(fileRef:FileReference){ 
  30.             socket = new Socket; 
  31.             this.fileRef = fileRef; 
  32.             var scope = this
  33.             //监听是否连接     
  34.              socket.addEventListener(Event.CONNECT,function conn(){ 
  35.                 var temp = scope.fileRef; 
  36.                  //发送名称   
  37.                  socket.writeUTF(temp.name); 
  38.                  socket.flush(); 
  39.                  //文件大小  
  40.                  try
  41.                     socket.writeUTF(String(temp.data.length));   
  42.                 }catch(e:Error){ 
  43.                     Alert.show(e.message); 
  44.                     Alert.show(e.getStackTrace()); 
  45.                 } 
  46.                   
  47.                 socket.flush(); 
  48.                 
  49.              });    
  50.               
  51.               
  52.              //监听接受数据   
  53.              socket.addEventListener(ProgressEvent.SOCKET_DATA,function receiveData(){ 
  54.                                             
  55.                      //已上传大小   
  56.                      var len:String = socket.readUTF(); 
  57. //                   Alert.show(int(uint(len) / fileRef.data.length * 100)  + "") 
  58.                      
  59.                      if(len == "0"){ 
  60.                           if(fileRef.data.length < 1024){ 
  61.                                   socket.writeBytes(fileRef.data);   
  62.                           }else
  63.                                       socket.writeBytes(fileRef.data,0,1024);  
  64.                            }   
  65.                           socket.flush();   
  66.                      }else
  67.                          if((fileRef.data.length - uint(len)) > 1024){ 
  68.                             socket.writeBytes(fileRef.data,uint(len),1024);   
  69.                             socket.flush(); 
  70.                          }else
  71.                              socket.writeBytes(fileRef.data,uint(len), fileRef.data.length - uint(len)); 
  72.                              socket.flush(); 
  73.                              setStatus(); 
  74.                          }   
  75.                           
  76.                       }   
  77.                        progressBar.setProgress(getProcess(uint(len),fileRef.data.length),100); 
  78.                                            
  79.              });  
  80.               
  81.              socket.addEventListener(Event.CLOSE,function close(){ 
  82. //              Alert.show("传送完毕") 
  83.              }) 
  84.         } 
  85.          
  86.         private function getProcess(len , length):int
  87.             var result ; 
  88.             if(length - len < 1024){ 
  89.                 result = 100; 
  90.             }else
  91.                 result = int(uint(len) / fileRef.data.length * 100);         
  92.             } 
  93.             return result; 
  94.         } 
  95.          
  96.         public function doUpload(bar , fn){ 
  97.             progressBar = bar; 
  98.             successMethod = fn; 
  99.             progressBar.visible = true
  100. //          socket..connect("192.168.0.121",2424); //连接服务器 
  101.             socket..connect("127.0.0.1",2424); //连接服务器    
  102.         } 
  103.          
  104.          
  105.     } 

这是一个AS类,在构造函数中传入上传文件的对象,并初始化两个监听事件。

flex.mxml

 

  
  
  
  
  1. <?xml version="1.0" encoding="utf-8"?> 
  2. <mx:Application backgroundColor="#FFFFFF" backgroundAlpha="2" xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="474" height="310" 
  3.     > 
  4.     <mx:Button id="uploadds" label="选择..." fontSize="12" click="fileBrowse()" x="117" y="42" height="33" width="82"/> 
  5.     <mx:Button id="doUpload" label="上传" fontSize="12" enabled="false" click="toUpload()" x="207" y="42" height="33" width="82"/> 
  6.     <mx:Label id="uploadLabel" text="请选择文件" fontSize="12" x="117" y="10" height="24" width="82"/> 
  7.     <mx:ProgressBar id="bar" labelPlacement="bottom" x="117" y="102" visible="false" mode="manual"/> 
  8.     <mx:Script> 
  9.         <![CDATA[ 
  10.             import mx.controls.Alert; 
  11.             import com.Upload; 
  12.             import flash.net.Socket; 
  13.             import flash.external.ExternalInterface; 
  14.             private var fileRef:FileReference ; 
  15.              
  16.              
  17.             public function fileBrowse():void{ 
  18.                 fileRef = new FileReference(); 
  19.                 fileRef.addEventListener(Event.SELECT, function select(){ 
  20.                     fileRef.load(); 
  21.                     uploadLabel.text  =  "加载文件中...." 
  22.                 }); 
  23.                 fileRef.addEventListener(Event.COMPLETE,function onComplete(){ 
  24.                     uploadLabel.text = fileRef.name; 
  25.                     doUpload.enabled = true; 
  26.                 }); 
  27.          
  28.     //          var gifFilter:FileFilter = new FileFilter("GIF Images", "*.gif", "GIFF"); 
  29.                 this.fileRef.browse(); 
  30.         } 
  31.              
  32.              
  33.             private function toUpload():void{ 
  34.                 if(fileRef.data == null){ 
  35. //                  Alert.("请选择文件") 
  36.                     this.fileBrowse(); 
  37.                     return; 
  38.                 } 
  39.                 var upload:Upload = new Upload(fileRef); 
  40.                 upload.doUpload(bar , setSuccess); 
  41.             } 
  42.              
  43.             private function setSuccess():void{ 
  44.                 ExternalInterface.call("setUploadFileName",fileRef.name); 
  45.                 uploadLabel.text = "上传成功" 
  46.                 doUpload.enabled= false; 
  47.             } 
  48.         ]]> 
  49.     </mx:Script> 
  50.      
  51. </mx:Application> 

 

这是flex的mxml文件,点击“选择”按钮后,会弹出文件选择框,选择你要上传的文件后,程序会读取该文件的内容(这里的读取应该是一次性读取所有文件内容,所以文件较大时,会多花几秒钟)。点击上传按钮则开始与服务器进行连接。

 LargeFileUploadPolicyListener.java

 

  
  
  
  
  1. package com; 
  2.  
  3. import java.net.ServerSocket;   
  4. import java.net.Socket;   
  5. import javax.servlet.ServletContextEvent;   
  6.    
  7. public class LargeFileUploadPolicyListener extends javax.servlet.http.HttpServlet implements javax.servlet.ServletContextListener{   
  8.    
  9.    
  10.     private static final long serialVersionUID = 1L;   
  11.     private static Thread thread = null;   
  12.    
  13.     @SuppressWarnings("deprecation")   
  14.     public void contextDestroyed(ServletContextEvent arg0) {   
  15.         if(thread != null){   
  16.             thread = null;   
  17.         }   
  18.     }   
  19.    
  20.     public void contextInitialized(ServletContextEvent arg0) {   
  21.         try {     
  22.             thread = new Thread() {   
  23.                 public void run() {   
  24.                     System.out.println("大文件上传侦听开始。。。。"); 
  25.                     try{   
  26.                         ServerSocket policyServerSocket= new ServerSocket(Integer.parseInt("843"));//服务器套接字   
  27.                            
  28.                         Socket policyClientSocket = null;   
  29.                            
  30.                         Socket clientSocket=null;     
  31.                         while(true){     
  32.                                
  33.                             policyClientSocket = policyServerSocket.accept(); //获得客户端的请求的Socket    
  34.                             System.out.println("已侦听到了客户端的请求。。。。。"); 
  35.                             new MyPolicyServerThread(policyClientSocket);   
  36.                         }     
  37.                     }catch (Exception e) {   
  38.                         e.printStackTrace(); 
  39.                     }   
  40.                 }   
  41.             };   
  42.             thread.start();   
  43.         } catch (Exception e) {     
  44. //            log.error("启动监听器异常:",e);   
  45.             e.printStackTrace(); 
  46.         }    
  47.     }   
  48. }   

随着服务器的启动而启动,开始侦听843端口的连接。

MyPolicyServerThread.java

 

  
  
  
  
  1. package com; 
  2.  
  3. import java.io.BufferedReader;   
  4. import java.io.IOException;   
  5. import java.io.InputStreamReader;   
  6. import java.io.PrintStream;   
  7. import java.io.UnsupportedEncodingException;   
  8. import java.net.Socket;   
  9.    
  10.    
  11. public class MyPolicyServerThread extends Thread {   
  12.    
  13.     private Socket socket;   
  14.        
  15.      private final String policy_xml = "<policy-file-request/>";      
  16.      private final String cross_xml = "<?xml version=\"1.0\"?>" +   
  17.             "<cross-domain-policy>" +   
  18.             "<site-control permitted-cross-domain-policies=\"all\"/>" +   
  19.             "<allow-access-from domain=\"*\" to-ports=\"2424\"/>" +   
  20.             "</cross-domain-policy>\0";   
  21.    
  22.     public MyPolicyServerThread(Socket socket) { 
  23.         this.socket = socket;   
  24.         this.start();   
  25.     }   
  26.    
  27.     @Override   
  28.     public void run() { 
  29.         try { 
  30.             BufferedReader readerIn = new BufferedReader(new InputStreamReader(socket.getInputStream()));   
  31.             PrintStream printOut = new PrintStream(socket.getOutputStream());   
  32.    
  33.             char[] by = new char[22];   
  34.             readerIn.read(by, 022);   
  35.             String s = new String(by);   
  36.             if (s.equals(policy_xml)) {   
  37.                 System.out.println("接收policy-file-request认证");   
  38.                 printOut.print(cross_xml);   
  39.                 printOut.flush();   
  40.                 readerIn.close();   
  41.                 printOut.close();   
  42.                 socket.close();   
  43.                 System.out.println("完成policy-file-request认证");   
  44.             }    
  45.    
  46.         } catch (UnsupportedEncodingException e) { 
  47.             e.printStackTrace();   
  48.         } catch (IOException e) {   
  49.             e.printStackTrace(); 
  50.         }   
  51.     }   
  52. }   

接收客户端发送过来的认证请求并进行验证。注意cross_xml 变量,客户端与服务器传送文件的端口就在这里设置的。

LargeFileUploadListener.java

 

  
  
  
  
  1. package com; 
  2.  
  3.  
  4.  import java.net.ServerSocket;   
  5. import java.net.Socket;   
  6. import javax.servlet.ServletContextEvent;   
  7.  
  8. public class LargeFileUploadListener extends javax.servlet.http.HttpServlet implements javax.servlet.ServletContextListener{   
  9.    
  10.      private static final long serialVersionUID = 1L;   
  11.   private static Thread thread = null
  12.   
  13.      @SuppressWarnings("deprecation")   
  14.     public void contextDestroyed(ServletContextEvent arg0) {   
  15.          if(thread != null){   
  16.              thread = null;   
  17.         }   
  18.     }   
  19.     
  20.      public void contextInitialized(ServletContextEvent arg0) { 
  21.         try {     
  22.             thread = new Thread() {   
  23.                 public void run() { 
  24.                     System.out.println("大文件上传侦听开始"); 
  25.                      try
  26.                  
  27.                          ServerSocket serverSocket= new ServerSocket(Integer.parseInt("2424"));//服务器套接字     
  28.                        Socket clientSocket=null;     
  29.                          while(true){     
  30.                             clientSocket= serverSocket.accept();//获得客户端的请求的Socket     
  31.                              System.out.println("已侦听到了客户端的请求。。。。。"); 
  32.                             new MyServerThread(clientSocket);     
  33.                        }     
  34.                     }catch (Exception e) {   
  35.                        e.printStackTrace(); 
  36.                      }   
  37.                  }   
  38.              };   
  39.             thread.start();   
  40.          } catch (Exception e) {     
  41.              e.printStackTrace(); 
  42.          }    
  43.      }   
  44.         
  45.  }   

随着服务器的启动而运行,侦听文件传送端口。

MyServerThread.java

 

  
  
  
  
  1. package com; 
  2.  
  3.  
  4.  import java.io.DataInputStream;   
  5.  import java.io.DataOutputStream;   
  6.  import java.io.File;   
  7.  import java.io.FileInputStream;   
  8.  import java.io.IOException;   
  9.  import java.io.InputStream;   
  10.  import java.io.RandomAccessFile;   
  11.  import java.net.Socket;   
  12.  import java.nio.channels.FileChannel;   
  13.  import java.nio.channels.FileLock;   
  14.  import java.util.Map;   
  15.  import java.util.StringTokenizer;   
  16.    
  17.     
  18.     
  19.  public class MyServerThread extends Thread {   
  20.     
  21.     private Socket socket;   
  22.     
  23.      public MyServerThread(Socket socket ) {   
  24.          this.socket = socket;   
  25.          this.start(); 
  26.     }   
  27.      @Override   
  28.      public void run() { 
  29.        
  30.             System.out.println("run"); 
  31.             DataInputStream dataInputStream = null;   
  32.              DataOutputStream dataOutputStream = null;   
  33.              RandomAccessFile randomAccessFile = null;   
  34.              FileLock fileLock = null
  35.              FileChannel fileChannel =null
  36.                
  37.              boolean isInfoSubmission = false;   
  38. //            Document docInfoSubmission = null;   
  39.              Double totalSize = 0.0;   
  40. //            DocProperty docProperty = null;   
  41.             
  42.              try { 
  43.                  dataInputStream = new DataInputStream(socket.getInputStream());   
  44.                  dataOutputStream = new DataOutputStream(socket.getOutputStream());    
  45.                    
  46.                  //读取名称   
  47.                  String fileName = dataInputStream.readUTF();   
  48.                    
  49.                  String fileSize = dataInputStream.readUTF();   
  50.                   
  51.                  File f = new File(this.getClass().getResource("").getPath());  
  52.                  System.out.println(f.getPath().substring(0,f.getPath().indexOf("WEB-INF"))+"upload/");  
  53.                   
  54.                  //检测上传文件是否存在   
  55.                  String FilePath = f.getPath().substring(0,f.getPath().indexOf("WEB-INF"))+"upload";  //可使用配置文件形式将路径写清楚   
  56.                  StringTokenizer st = new StringTokenizer(FilePath.toString(),"/");      
  57.                  String   toAddPath = st.nextToken()+"/";      
  58.                 String   toTestPath = toAddPath;      
  59.                 while(st.hasMoreTokens()){ 
  60.                      toAddPath = st.nextToken()+"/";      
  61.                       toTestPath += toAddPath;      
  62.                       File inbox   =   new File(toTestPath);      
  63.                        if(!inbox.exists()) {   
  64.                           inbox.mkdir();      
  65.                        }   
  66.                  }     
  67.                     
  68.                          //检测上传位置   
  69.                          File file = new File( FilePath + "/" + fileName);   
  70.                 long position = 0;   
  71.                    
  72.                  if(file.exists()){ 
  73.                         position = file.length();   
  74.                  }   
  75.                     
  76.                 //通知客户端已传大小   
  77.                  dataOutputStream.writeUTF(String.valueOf(position));   
  78.                  dataOutputStream.flush();   
  79.         
  80.                  byte[] buffer = null;   
  81.                 int read = 0;   
  82.                     
  83.                  while(true){ 
  84.                     //检测上传位置   
  85.                     file = new File( FilePath + "/" + fileName); 
  86.                     position = 0
  87.                      if(file.exists()){ 
  88.                          position = file.length();   
  89.                     }   
  90.                         
  91.                         //rw代表写流(随机读写)   
  92.                      randomAccessFile  = new RandomAccessFile(file,"rw");   
  93.                         
  94.                      fileLock = null;   
  95.                      fileChannel = null;    
  96.                      fileChannel = randomAccessFile.getChannel();   
  97.                      fileLock = fileChannel.tryLock();   
  98.                        
  99.                      //拿到了文件锁,写入数据   
  100.                     if(fileLock != null){   
  101.                         randomAccessFile.seek(position);   
  102.                            
  103.                      read = 0;   
  104.                          buffer = new byte[1024];   
  105.                          read = dataInputStream.read(buffer);   
  106.                         randomAccessFile.write(buffer,0,read);   
  107.                         if(fileLock != null){   
  108.                              fileLock.release();   
  109.                             fileLock = null;   
  110.                          }   
  111.                          if(randomAccessFile != null){   
  112.                             randomAccessFile.close();   
  113.                              randomAccessFile = null;   
  114.                          }   
  115.                     }      
  116.                        
  117.                      //检测已上传的大小   
  118.                        file = new File( FilePath + "/" + fileName);   
  119.                     position = 0;   
  120.                     if(file.exists()){   
  121.                              position = file.length();   
  122.                      }   
  123. //                     System.out.println("文件  " + fileName + "  已传输  " + String.valueOf(position)+ "字节");   
  124.                         
  125.                       
  126.                      //判断文件是否传输完成   
  127.                      if(position >= Long.parseLong(fileSize)){   
  128.                           //文件传输完成   
  129.                            System.out.println("文件  " + fileName + "  已传输完毕,总大小为" + String.valueOf(position) + "字节");   
  130.                              break ;   
  131.                      }else{   
  132.                              //通知客户端已传大小   
  133.                         dataOutputStream.writeUTF(String.valueOf(position)); 
  134.                          dataOutputStream.flush();   
  135.                      } 
  136.                 }   // END WHILE   
  137.                    
  138.                 //跳出while循环,即已结束文件上传,则终止socket通信   
  139.                  
  140.                    
  141.             } catch (Exception e) {   
  142.                 // TODO Auto-generated catch block   
  143.                 e.printStackTrace();   
  144.             }finally
  145.                 if(fileLock!=null){ 
  146.                      
  147.                 } 
  148.                 try { 
  149.                     if(fileChannel!=null){ 
  150.                         fileChannel.close(); 
  151.                     } 
  152.                     if(randomAccessFile!=null){ 
  153.                         randomAccessFile.close(); 
  154.                     } 
  155.                     dataInputStream.close(); 
  156.                      dataOutputStream.close();   
  157.                     socket.close();  
  158.                 } catch (IOException e) { 
  159.                     // TODO Auto-generated catch block 
  160.                     e.printStackTrace(); 
  161.                 }   
  162.             } 
  163.     }   
  164.  }   

接收上传文件类。

你可能感兴趣的:(java,socket,Flex,断点上传,flashplayer)