加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

Flex + Servlet 实现断点上传

发布时间:2020-12-15 04:23:56 所属栏目:百科 来源:网络整理
导读:如果只是上传大文件,使用cos是很好实现的。cos的上传原理是边读边写,开发人员可以自己分配缓存的大

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

但是后来要求实现断点续传的功能,我尝试了使用HTTP协议,不行,我印象中,HTTP协议来说,应该不好实现断点上传。后来查找资料,发现许多人使用ftp协议。如果要使用ftp,又需要在客户端安装applet,据说applet太麻烦也不太好用,applet一般很少有人去开发这个了还需要jvm环境。

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

因为现在很多浏览器都是支持flash的,所以都会安装了flash的activeX组件。起作用的 就应该是activeX组件了。

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

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

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

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

安全策略认证如下:

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

 
 
  1. <policy-file-request/>??

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

?

<?xml?version="1.0"?>?
  
  
  • cross-domain-policy>?
  • site-control?permitted-cross-domain-policies="all"/>?
  • allow-access-from?domain="*"?to-ports="2424"/>?
  • </>?
  • 注意两个地方,to-ports表示客户端通过该端口向服务器发送接收数据。另外,结尾处的必须添加。

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

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

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

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

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

    下面上代码:

    FinderLargerUpload.as

    package cn.cnxingkong.finder.upload
    {
    	import flash.events.Event;
    	import flash.events.ProgressEvent;
    	import flash.net.FileReference;
    	import flash.net.Socket;
    	
    	import mx.controls.Alert;
    	import mx.controls.ProgressBar;
    	
    	import org.flexunit.internals.namespaces.classInternal;
    
    	public class FinderLargerUpload
    	{
    		
    		private var socket:Socket ;      
    		private var fileRef:FileReference ; 
    		private var progressBar:ProgressBar; 
    		private var status:int = 0;//上传成功状态 
    		private var successMethod:Function; 
    		
    		
    		
    		private var uploadCompletedMethod:Function;
    		
    		private var host:String="127.0.0.1";
    		
    		private var port:int=80;
    		
    		
    		public function getStatus():int{ 
    			return status; 
    		} 
    		
    		public function setStatus():void{ 
    			status = 1; 
    			successMethod.call(); 
    		} 
    		
    		public function setHost(pHost:String):void{
    			this.host = pHost;
    		}
    		
    		public function setPort(pPort:int):void{
    			
    			this.port = pPort;
    		}
    		
    		/***
    		 * 暂时不使用
    		 * 
    		 */
    		public function setUploadCompletedMethod(pUploadCompletedMethod:Function):void
    		{
    			this.uploadCompletedMethod = pUploadCompletedMethod;
    		}
    		
    		
    		public function FinderLargerUpload(fileRef:FileReference){ 
    			socket = new Socket; 
    			this.fileRef = fileRef; 
    			var scope = this; 
    			//监听是否连接     
    			socket.addEventListener(Event.CONNECT,function conn(){ 
    				var temp:FileReference = scope.fileRef; 
    				//发送名称   
    				socket.writeUTF(temp.name); 
    				socket.flush(); 
    				//文件大小  
    				try{ 
    					socket.writeUTF(String(temp.data.length));   
    				}catch(e:Error){ 
    					Alert.show(e.message); 
    					//Alert.show(e.getStackTrace()); 
    				} 
    				
    				socket.flush(); 
    				
    			});    
    			
    			
    			//监听接受数据   
    			socket.addEventListener(ProgressEvent.SOCKET_DATA,function receiveData():void{ 
    				
    				//已上传大小   
    				var len:String = socket.readUTF(); 
    				//                   Alert.show(int(uint(len) / fileRef.data.length * 100)  + "") 
    				
    				if(len == "0"){ 
    					if(fileRef.data.length < 1024){ 
    						socket.writeBytes(fileRef.data);   
    					}else{ 
    						socket.writeBytes(fileRef.data,1024);  
    					}   
    					socket.flush();   
    				}else{ 
    					if((fileRef.data.length - uint(len)) > 1024){ 
    						socket.writeBytes(fileRef.data,uint(len),1024);   
    						socket.flush(); 
    					}else{ 
    						socket.writeBytes(fileRef.data,fileRef.data.length - uint(len)); 
    						socket.flush(); 
    						setStatus(); 
    					}   
    					
    				}   
    				var currPos:Number = getProcess(uint(len),fileRef.data.length);
    				progressBar.setProgress(currPos,100); 
    				//progressBar.label= (currPos/100)*100 +"%";
    				
    			});  
    			
    			socket.addEventListener(Event.CLOSE,function close():void{
    				if(uploadCompletedMethod==null)
    					uploadCompletedMethod.call();
    							  
    			});
    		} 
    		
    		private function getProcess(len:int,length:int):int{ 
    			var result:int ; 
    			if(length - len < 1024){ 
    				result = 100; 
    			}else{ 
    				result = int(uint(len) / fileRef.data.length * 100);         
    			} 
    			return result; 
    		} 
    		
    		public function doUpload(bar,fn){ 
    			progressBar = bar; 
    			successMethod = fn; 
    			progressBar.visible = true; 
    			//          socket..connect("192.168.0.121",2424); //连接服务器 
    			socket.connect(host,port); //连接服务器  
    		} 
    		
    		public function getSocket():Socket{
    			return this.socket;
    		}
    		
    		//关闭socket,停止上传
    		public function close():void{
    			this.socket.close();
    		}
    		
    	}
    }


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

    UploadApp.mxml

    <?xml version="1.0" encoding="utf-8"?>
    <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
    			   xmlns:s="library://ns.adobe.com/flex/spark" 
    			   xmlns:mx="library://ns.adobe.com/flex/mx" 
    			  backgroundColor="234223"
    			    width="474" height="310">
    	<s:layout>
    		<s:BasicLayout/>
    	</s:layout>
    	<fx:Declarations>
    		<!-- 将非可视元素(例如服务、值对象)放在此处 -->
    	</fx:Declarations>
    	
    	
    	<fx:Script>
    		<![CDATA[
    			import cn.cnxingkong.finder.upload.FinderLargerUpload;
    			
    			
    			import flash.external.ExternalInterface;
    			import flash.net.Socket;
    			
    			import mx.controls.Alert; 
    			
    			
    			private var fileRef:FileReference ; 
    			
    			
    			public function fileBrowse():void{ 
    				fileRef = new FileReference(); 
    				fileRef.addEventListener(Event.SELECT,function select(){
    					
    					fileRef.load();
    					uploadLabel.text  =  "加载文件中...." 
    				}); 
    				fileRef.addEventListener(Event.COMPLETE,function onComplete(){ 
    					uploadLabel.text = fileRef.name; 
    					doUpload.enabled = true; 
    					doPause.enabled = true;
    				}); 
    				
    				//          var gifFilter:FileFilter = new FileFilter("GIF Images","*.gif","GIFF"); 
    				this.fileRef.browse(); 
    			} 
    			
    			var upload:FinderLargerUpload;
    			private function toUpload():void{ 
    				if(fileRef.data == null){ 
    					//                  Alert.("请选择文件") 
    					this.fileBrowse(); 
    					return; 
    				} 
    				upload = new FinderLargerUpload(fileRef); 
    				upload.setHost("127.0.0.1");
    				upload.setPort(2424);
    				upload.doUpload(bar,setSuccess);
    				
    				
    			} 
    			
    			private function pauseUpload():void{
    				upload.close();
    			}
    			
    			private function setSuccess():void{ 
    				ExternalInterface.call("setUploadFileName",fileRef.name);
    				uploadLabel.text = "上传成功" 
    				doUpload.enabled= false;
    				doPause.enabled =false;
    			} 
    		]]>
    	</fx:Script>
    	
    	
    	
    	
    	<mx:Button id="uploadds" label="选择..." fontSize="12" click="fileBrowse()" x="117" y="42" height="33" width="82"/> 
    	<mx:Button id="doUpload" label="上传" fontSize="12" enabled="false" click="toUpload()" x="207" y="42" height="33" width="82"/> 
    	<mx:Button id="doPause" label="停止" fontSize="12" enabled="false" click="pauseUpload()" x="300" y="42" height="33" width="82"/> 
    	<mx:Label id="uploadLabel" text="请选择文件" fontSize="12" x="117" y="10" height="24" width="82"/> 
    	<mx:ProgressBar id="bar" labelPlacement="center" x="117" y="102" visible="false"/> 
    </s:Application>
    



    ?

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

    ?LargeFileUploadPolicyListener.java

    package cn.cnxingkong.finder.largerupload;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    import javax.servlet.ServletException;
    
    import org.apache.log4j.Logger;
    
    
    public class LargeFileUploadPolicyListener extends javax.servlet.http.HttpServlet{   
       
       
        /**
    	 * 
    	 */
    	private static final long serialVersionUID = 4220986582714447557L;
    	
    	private static Logger logger = Logger.getLogger(LargeFileUploadPolicyListener.class);
    	
    	private static Thread thread = null;  
    	
    	
       /*
        public void contextDestroyed(ServletContextEvent arg0) {   
            if(thread != null){   
                thread = null;   
            }   
        }   
       
        public void contextInitialized(ServletContextEvent arg0) {   
            try {     
                thread = new Thread() {   
                    public void run() {   
                        System.out.println("大文件上传侦听开始。。。。"); 
                        try{   
                            ServerSocket policyServerSocket= new ServerSocket(Integer.parseInt("843"));//服务器套接字   
                               
                            Socket policyClientSocket = null;   
                               
                            Socket clientSocket=null;     
                            while(true){     
                                   
                                policyClientSocket = policyServerSocket.accept(); //获得客户端的请求的Socket    
                                System.out.println("已侦听到了客户端的请求。。。。。"); 
                                new MyPolicyServerThread(policyClientSocket);   
                            }     
                        }catch (Exception e) {   
                            e.printStackTrace(); 
                        }   
                    }   
                };   
                thread.start();   
            } catch (Exception e) {     
    //            log.error("启动监听器异常:",e);   
                e.printStackTrace(); 
            }    
        }   */
    	
    	public void init() throws ServletException {
    		try {     
                thread = new Thread() {   
                    public void run() {   
                        logger.info("大文件上传策略认证侦听开始。。。。"); 
                        try{   
                            ServerSocket policyServerSocket= new ServerSocket(Integer.parseInt("843"));//服务器套接字   
                               
                            Socket policyClientSocket = null;   
                            while(true){     
                                policyClientSocket = policyServerSocket.accept(); //获得客户端的请求的Socket    
                                logger.info("已侦听到了客户端策略认证的请求。。。。。"); 
                                new MyPolicyServerThread(policyClientSocket);   
                            }     
                        }catch (Exception e) {   
                           logger.error(e.toString()); 
                        }   
                    }   
                };   
                thread.start();   
            } catch (Exception e) {     
            	  logger.error(e.toString());
            }
    	}
    }   
    

    ?

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

    MyPolicyServerThread.java

    package cn.cnxingkong.finder.largerupload;
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.PrintStream;
    import java.io.UnsupportedEncodingException;
    import java.net.Socket;
    
    import org.apache.log4j.Logger;
    
    
    public class MyPolicyServerThread extends Thread {   
    	private static Logger logger = Logger.getLogger(LargeFileUploadPolicyListener.class);
        private Socket socket;   
           
         private final String policy_xml = "<policy-file-request/>";      
         private final String cross_xml = "<?xml version="1.0"?>" +   
                "<cross-domain-policy>" +   
                "<site-control permitted-cross-domain-policies="all"/>" +   
                "<allow-access-from domain="*" to-ports="2424"/>" +   
                "</cross-domain-policy>";   
       
        public MyPolicyServerThread(Socket socket) { 
            this.socket = socket;   
            this.start();   
        }   
       
        @Override   
        public void run() { 
            try { 
                BufferedReader readerIn = new BufferedReader(new InputStreamReader(socket.getInputStream()));   
                PrintStream printOut = new PrintStream(socket.getOutputStream());   
       
                char[] by = new char[22];   
                readerIn.read(by,22);   
                String s = new String(by);   
                if (s.equals(policy_xml)) {   
                    logger.info("接收policy-file-request认证");   
                    printOut.print(cross_xml);   //发送 cross_xml
                    printOut.flush();   
                    readerIn.close();   
                    printOut.close();   
                    socket.close();   
                    logger.info("完成policy-file-request认证");   
                }    
       
            } catch (Exception e) { 
            	 logger.error(e.toString()); 
            }   
        }   
    }   
    



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

    LargeFileUploadListener.java

    package cn.cnxingkong.finder.largerupload;
    
    import java.net.ServerSocket;
    import java.net.Socket;
    
    import javax.servlet.ServletException;
    
    import org.apache.log4j.Logger;
     
    public class LargeFileUploadListener extends javax.servlet.http.HttpServlet{   
    	private static Logger logger = Logger.getLogger(LargeFileUploadPolicyListener.class);
         private static final long serialVersionUID = 1L;   
         private static Thread thread = null;
    	@Override
    	public void init() throws ServletException {
    		   try {     
    	            thread = new Thread() {   
    	                public void run() { 
    	                    logger.info("大文件上传侦听开始"); 
    	                     try{ 
    	                 
    	                         ServerSocket serverSocket= new ServerSocket(Integer.parseInt("2424"));//服务器套接字     
    	                       Socket clientSocket=null;     
    	                         while(true){     
    	                            clientSocket= serverSocket.accept();//获得客户端的请求的Socket     
    	                             logger.info("已侦听到了客户端开始上传文件的请求。。。。。"); 
    	                            new MyServerThread(clientSocket);     
    	                       }     
    	                    }catch (Exception e) {   
    	                       e.printStackTrace(); 
    	                     }   
    	                 }   
    	             };   
    	            thread.start();   
    	         } catch (Exception e) {     
    	             logger.error(e.toString());
    	         }    
    	} 
      
       /*  @SuppressWarnings("deprecation")   
        public void contextDestroyed(ServletContextEvent arg0) {   
             if(thread != null){   
                 thread = null;   
            }   
        }   
        
         public void contextInitialized(ServletContextEvent arg0) { 
            try {     
                thread = new Thread() {   
                    public void run() { 
                        logger.info("大文件上传侦听开始"); 
                         try{ 
                     
                             ServerSocket serverSocket= new ServerSocket(Integer.parseInt("2424"));//服务器套接字     
                           Socket clientSocket=null;     
                             while(true){     
                                clientSocket= serverSocket.accept();//获得客户端的请求的Socket     
                                 logger.info("已侦听到了客户端的请求。。。。。"); 
                                new MyServerThread(clientSocket);     
                           }     
                        }catch (Exception e) {   
                           e.printStackTrace(); 
                         }   
                     }   
                 };   
                thread.start();   
             } catch (Exception e) {     
                 e.printStackTrace(); 
             }    
         }   
            */
         
         
     }   

    ?

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

    MyServerThread.java

    ?

    package cn.cnxingkong.finder.largerupload;
    
    import java.io.DataInputStream;
    import java.io.DataOutputStream;
    import java.io.File;
    import java.io.IOException;
    import java.io.RandomAccessFile;
    import java.net.Socket;
    import java.nio.channels.FileChannel;
    import java.nio.channels.FileLock;
    import java.util.StringTokenizer;
    
    import org.apache.log4j.Logger;
    
    public class MyServerThread extends Thread {
    	private static Logger logger = Logger
    			.getLogger(LargeFileUploadPolicyListener.class);
    	private Socket socket;
    	private String fileName;
    	private long position = 0;
    
    	private String uploadFileRootPath = "upload";
    
    	public void setUploadRootPath(String uploadFileRootPath) {
    		this.uploadFileRootPath = uploadFileRootPath;
    	}
    
    	public MyServerThread(Socket socket) {
    		this.socket = socket;
    		this.start();
    	}
    
    	@Override
    	public void run() {
    
    		logger.info("数据上传中。。。");
    		DataInputStream dataInputStream = null;
    		DataOutputStream dataOutputStream = null;
    		RandomAccessFile randomAccessFile = null;
    		FileLock fileLock = null;
    		FileChannel fileChannel = null;
    
    		boolean isInfoSubmission = false;
    		// Document docInfoSubmission = null;
    		Double totalSize = 0.0;
    		// DocProperty docProperty = null;
    
    		try {
    			dataInputStream = new DataInputStream(socket.getInputStream());
    			dataOutputStream = new DataOutputStream(socket.getOutputStream());
    
    			// 读取名称
    			fileName = dataInputStream.readUTF();
    
    			String fileSize = dataInputStream.readUTF();
    
    			File f = new File(this.getClass().getResource("").getPath());
    			logger.info(f.getPath()
    					.substring(0,f.getPath().indexOf("WEB-INF"))
    					+ uploadFileRootPath + "/"+fileName);
    
    			// 检测上传文件是否存在
    			String FilePath = f.getPath().substring(0,f.getPath().indexOf("WEB-INF"))
    					+ uploadFileRootPath; // 可使用配置文件形式将路径写清楚
    			StringTokenizer st = new StringTokenizer(FilePath.toString(),"/");
    			String toAddPath = st.nextToken() + "/";
    			String toTestPath = toAddPath;
    			while (st.hasMoreTokens()) {
    				toAddPath = st.nextToken() + "/";
    				toTestPath += toAddPath;
    				File inbox = new File(toTestPath);
    				if (!inbox.exists()) {
    					inbox.mkdir();
    				}
    			}
    
    			// 检测上传位置
    			File file = new File(FilePath + "/" + fileName);
    
    			if (file.exists()) {
    				position = file.length();
    			}
    
    			// 通知客户端已传大小
    			dataOutputStream.writeUTF(String.valueOf(position));
    			dataOutputStream.flush();
    
    			byte[] buffer = null;
    			int read = 0;
    
    			while (true) {
    				// 检测上传位置
    				file = new File(FilePath + "/" + fileName);
    				position = 0;
    				if (file.exists()) {
    					position = file.length();
    				}
    
    				// rw代表写流(随机读写)
    				randomAccessFile = new RandomAccessFile(file,"rw");
    
    				fileLock = null;
    				fileChannel = null;
    				fileChannel = randomAccessFile.getChannel();
    
    				// 保证当前只有自己操作此文件
    				fileLock = fileChannel.tryLock();
    
    				// 拿到了文件锁,写入数据
    				if (fileLock != null) {
    					randomAccessFile.seek(position);
    
    					read = 0;
    					buffer = new byte[1024];
    
    					if (!"0".equals(fileSize)) {
    						read = dataInputStream.read(buffer);
    						randomAccessFile.write(buffer,read);
    					}
    					if (fileLock != null) {
    						fileLock.release();
    						fileLock = null;
    					}
    					if (randomAccessFile != null) {
    						randomAccessFile.close();
    						randomAccessFile = null;
    					}
    				}
    
    				// 检测已上传的大小
    				file = new File(FilePath + "/" + fileName);
    				position = 0;
    				if (file.exists()) {
    					position = file.length();
    				}
    				// logger.info("文件  " + fileName + "  已传输  " +
    				// String.valueOf(position)+ "字节");
    
    				// 判断文件是否传输完成
    				if (position >= Long.parseLong(fileSize)) {
    					// 文件传输完成
    					logger.info("文件  " + fileName + "  已传输完毕,总大小为"
    							+ String.valueOf(position) + "字节");
    					break;
    				} else {
    					// 通知客户端已传大小
    					dataOutputStream.writeUTF(String.valueOf(position));
    					dataOutputStream.flush();
    				}
    			} // END WHILE
    
    			// 跳出while循环,即已结束文件上传,则终止socket通信
    
    		} catch (Exception e) {
    			// TODO Auto-generated catch block
    			logger.info("文件  " + fileName + " 传输中断,总大小为"
    					+ String.valueOf(position) + "字节");
    			  logger.error(e.toString());
    		} finally {
    			if (fileLock != null) {
    				try {
    					fileLock.release();
    					fileLock = null;
    					logger.info("文件  " + fileName + " lock release");
    				} catch (Exception e) {
    					  logger.error(e.toString());
    				}
    			}
    			try {
    				if (fileChannel != null) {
    					fileChannel.close();
    				}
    				if (randomAccessFile != null) {
    					randomAccessFile.close();
    				}
    				dataInputStream.close();
    				dataOutputStream.close();
    				socket.close();
    			} catch (Exception e) {
    				  logger.error(e.toString());
    			}
    		}
    	}
    }

    接收上传文件类。

    Web.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app version="2.5" 
    	xmlns="http://java.sun.com/xml/ns/javaee" 
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    	http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
      <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
      </welcome-file-list>
      
       <servlet>
    	    <servlet-name>largeFileUploadPolicyListener</servlet-name>
    	    <servlet-class>cn.cnxingkong.finder.largerupload.LargeFileUploadPolicyListener</servlet-class>
    	    <load-on-startup>3</load-on-startup>
    	</servlet>
    	
    	<servlet>
    	    <servlet-name>largeFileUploadListener</servlet-name>
    	    <servlet-class>cn.cnxingkong.finder.largerupload.LargeFileUploadListener</servlet-class>
    	    <load-on-startup>3</load-on-startup>
    	</servlet> 
    </web-app>
    

    (编辑:李大同)

    【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

      推荐文章
        热点阅读