Socket实现断点续传,Spring整合Socket

这段时间一直在做socket上传视频,搜索了许多资料,就以下两篇最典型

http://blog.csdn.net/geniusxiaoyu/article/details/7302946,

http://blog.csdn.net/zhjb1025/article/details/3244728, 

这期间会到许多问题,整理如下:

1. 启动用spring xml方式起动,进入socket无限循环出不来

2. spring依赖注入失效

3. application中有inin-method方法,运用ClassPathXmlApplicationContext不重复执行

整理以下内容:

服务端代码:

单独开启一个线程,避免socket阻塞线程

StartUpSocket.java

public class StartUpSocket {
	
	private int port;

	public StartUpSocket(){
		super();
	}
	
	public StartUpSocket(int port){
		this.port = port;
	}
	
//	@PostConstruct 相当于application.xml的init-method,二选一
	private void startUp() throws Exception{
		SocketThread thread = new SocketThread(port);
		thread.start();
	}

	public int getPort() {
		return port;
	}

	public void setPort(int port) {
		this.port = port;
	}
	
}

SocketThread.java

@Component("socketThread")
public class SocketThread extends Thread {

	private static Logger logger = LoggerFactory.getLogger(SocketThread.class);
	
	//这里Resource注入对象为null,有高手知道为什么吗?
//	@Resource(name = "socketServer")
	private SocketServer socket;
	
	private int port;
	
	public SocketThread(){
		count++;
	}
	
	public SocketThread(int port){
		this.port = port;
	}
	
	public void run(){
		try {
				socket = (SocketServer) ApplicationContextUtil.getSpringBean("socketServer");
				if(socket == null){
					logger.debug("*********** socket is null ***********");
				}else{
					socket.start(port);
				}
		} catch (Exception e) {
			socket.quit();
			logger.debug("An exception occurs, so socket is colsed");
		}
	}

	public int getPort() {
		return port;
	}

	public void setPort(int port) {
		this.port = port;
	}
	
}

这里关于加载ClassPathXmlApplicationContext,有两种方式,一种是上面用的写工具类(推荐),另外一种

new ClassPathXmlApplicationContext("application.xml"),因为有init-method,所以得统计这个类的加载次数,

否则会无限加载,还有一种用spring Resource,不知道为什么注入对象为null

第一种:

ApplicationContextUtil.java

@Component
public class ApplicationContextUtil implements ApplicationContextAware {

    //用static,在@Component时生成唯一对象,避免new生成多余对象
    private static ApplicationContext applicationContext = null;

    public static Object getSpringBean(String beanName) {
        return applicationContext.getBean(beanName);
    }

    @Autowired
    public void setApplicationContext(ApplicationContext appContext)
        throws BeansException {
        applicationContext = appContext;
   }
}

第二种:

@Component("socketThread")
public class SocketThread extends Thread {

	private static Logger logger = LoggerFactory.getLogger(SocketThread.class);
	
//      统计构造器加载次数
	private static int count = 0; 

	public static int getCount() { 
		return count; 
	}     
	public SocketThread(){
		count++;
	}
private SocketServer socket;private int port;public SocketThread(int port){this.port = port;}public void run(){try {//只加载一次application.xml文件,否则socket端口被占用if(count < 2){ApplicationContext cxt = new ClassPathXmlApplicationContext("application.xml");socket = (SocketServer) cxt.getBean("socketServer");if(socket == null){logger.debug("*********** socket is null ***********");}else{socket.start(port);}}} catch (Exception e) {socket.quit();logger.debug("An exception occurs, so socket is colsed");}}public int getPort() {return port;}public void setPort(int port) {this.port = port;}}
 
  
 
  

 
  
 
  

SocketServer

@Service("socketServer")
public class SocketServer {  
    private ExecutorService executorService;// 线程池  
    private ServerSocket ss = null;  
    private int port;// 监听端口  
    private boolean quit;// 是否退出  
    private Map datas = new HashMap();// 存放断点数据,最好改为数据库存放  
  
    private static Logger logger = LoggerFactory.getLogger(SocketServer.class);
    
    public SocketServer(int port) {  
        this.port = port;  
        // 初始化线程池  
        executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 200);  
    }  
  
    // 启动服务  
    public void start() throws Exception {  
        ss = new ServerSocket(port);  
        while (!quit) {  
            Socket socket = ss.accept();// 接受客户端的请求  
            // 为支持多用户并发访问,采用线程池管理每一个用户的连接请求  
            executorService.execute(new SocketTask(socket));// 启动一个线程来处理请求  
        }  
    }  
  
    // 退出  
    public void quit() {  
        this.quit = true;  
        try {  
            ss.close();  
        } catch (IOException e) {  
            e.printStackTrace();  
        }  
    }  
  
    private class SocketTask implements Runnable {  
        private Socket socket;  
  
        public SocketTask(Socket socket) {  
            this.socket = socket;  
        }  
  
        @Override  
        public void run() {  
            try {  
                logger.debug("accepted connenction from "  
                        + socket.getInetAddress() + " @ " + socket.getPort());  
                PushbackInputStream inStream = new PushbackInputStream(  
                        socket.getInputStream());  
                // 得到客户端发来的第一行协议数据:Content-Length=143253434;filename=xxx.3gp;sourceid=  
                // 如果用户初次上传文件,sourceid的值为空。  
                String head = StreamTool.readLine(inStream);  
                logger.debug("***head:"+head);  
                if (head != null) {  
                    // 下面从协议数据中读取各种参数值  
                    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);  
                    Long id = System.currentTimeMillis();  
                    FileLog log = null;  
                    if (null != sourceid && !"".equals(sourceid)) {  
                        id = Long.valueOf(sourceid);  
                        log = find(id);//查找上传的文件是否存在上传记录  
                    }  
                    File file = null;  
                    int position = 0;  
                    if(log==null){//如果上传的文件不存在上传记录,为文件添加跟踪记录  
                        String path = new SimpleDateFormat("yyyy/MM/dd/HH/mm").format(new Date());  
                        File dir = new File("file/"+ path);  
                        if(!dir.exists()) dir.mkdirs();  
                        file = new File(dir, filename);  
                        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);  
                    }else{// 如果上传的文件存在上传记录,读取上次的断点位置  
                        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));  
                                position = Integer.valueOf(properties.getProperty("length"));//读取断点位置  
                            }  
                        }  
                    }  
                      
                    OutputStream outStream = socket.getOutputStream();  
                    String response = "sourceid="+ id+ ";position="+ position+ "\r\n";  
                    //服务器收到客户端的请求信息后,给客户端返回响应信息:sourceid=1274773833264;position=0  
                    //sourceid由服务生成,唯一标识上传的文件,position指示客户端从文件的什么位置开始上传  
                    outStream.write(response.getBytes());  
                      
                    RandomAccessFile fileOutStream = new RandomAccessFile(file, "rwd");  
                    if(position==0) fileOutStream.setLength(Integer.valueOf(filelength));//设置文件长度  
                    fileOutStream.seek(position);//移动文件指定的位置开始写入数据  
                    byte[] buffer = new byte[1024];  
                    int len = -1;  
                    int length = position;  
                    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"));  
                        properties.store(logFile, null);//实时记录文件的最后保存位置  
                        logFile.close();  
                    }  
                    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) {}  
            }  
        }  
  
    }  
  
    public FileLog find(Long sourceid) {  
        return datas.get(sourceid);  
    }  
  
    // 保存上传记录  
    public void save(Long id, File saveFile) {  
        // 日后可以改成通过数据库存放  
        datas.put(id, new FileLog(id, saveFile.getAbsolutePath()));  
    }  
  
    // 当文件上传完毕,删除记录  
    public void delete(long sourceid) {  
        if (datas.containsKey(sourceid))  
            datas.remove(sourceid);  
    }  
  
    private class FileLog {  
        private Long id;  
        private String path;  
          
        public FileLog(Long id, String path) {  
            super();  
            this.id = id;  
            this.path = path;  
        }  
  
        public Long getId() {  
            return id;  
        }  
  
        public void setId(Long id) {  
            this.id = id;  
        }  
  
        public String getPath() {  
            return path;  
        }  
  
        public void setPath(String path) {  
            this.path = path;  
        }  
  
    }  
    
    /*public static void main(String[] args) throws Exception {  
        SocketServer server = new SocketServer(8787);  
        server.start();  
    }*/
    
}  

StreamTool.java

StreamTool {  
    
    public static void save(File file, byte[] data) throws Exception {  
        FileOutputStream outStream = new FileOutputStream(file);  
        outStream.write(data);  
        outStream.close();  
    }  
      
    public static String readLine(PushbackInputStream in) throws IOException {  
           char buf[] = new char[128];  
           int room = buf.length;  
           int offset = 0;  
           int c;  
loop:       while (true) {  
               switch (c = in.read()) {  
                   case -1:  
                   case '\n':  
                       break loop;  
                   case '\r':  
                       int c2 = in.read();  
                       if ((c2 != '\n') && (c2 != -1)) in.unread(c2);  
                       break loop;  
                   default:  
                       if (--room < 0) {  
                           char[] lineBuffer = buf;  
                           buf = new char[offset + 128];  
                           room = buf.length - offset - 1;  
                           System.arraycopy(lineBuffer, 0, buf, 0, offset);  
                            
                       }  
                       buf[offset++] = (char) c;  
                       break;  
               }  
           }  
           if ((c == -1) && (offset == 0)) return null;  
           return String.copyValueOf(buf, 0, offset);  
   }  
      
   /** 
   * 读取流 
   * @param inStream 
   * @return 字节数组 
   * @throws Exception 
   */  
   public static byte[] readStream(InputStream inStream) throws Exception{  
           ByteArrayOutputStream outSteam = new ByteArrayOutputStream();  
           byte[] buffer = new byte[1024];  
           int len = -1;  
           while( (len=inStream.read(buffer)) != -1){  
               outSteam.write(buffer, 0, len);  
           }  
           outSteam.close();  
           inStream.close();  
           return outSteam.toByteArray();  
   }  
} 

spring.xml



	

客户端代码:

布局文件layout/main.xml

  
  
  
      
      
          
    

数据文件values/strings.xml

  
  
    Hello World, UploadActivity!  
    大视频文件断点上传  
    文件名称  
    上传  
    SDCard不存在或者写保护  
    上传完成  
    上传失败  
    文件不存在  
AndroidManifest.xml

  
  
  
      
  
      
          
              
                  
  
                  
              
          
      
      
      
      
      
      
      
UploadActivity.java

public class UploadActivity extends Activity {  
    private EditText filenameText;  
    private TextView resulView;  
    private ProgressBar uploadbar;  
    private UploadLogService logService;  
    private Handler handler = new Handler(){  
        @Override  
        public void handleMessage(Message msg) {  
            int length = msg.getData().getInt("size");  
            uploadbar.setProgress(length);  
            float num = (float)uploadbar.getProgress()/(float)uploadbar.getMax();  
            int result = (int)(num * 100);  
            resulView.setText(result+ "%");  
            if(uploadbar.getProgress()==uploadbar.getMax()){  
                Toast.makeText(UploadActivity.this, R.string.success, 1).show();  
            }  
        }  
    };  
      
    @Override  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.main);  
          
        logService = new UploadLogService(this);  
        filenameText = (EditText)this.findViewById(R.id.filename);  
        uploadbar = (ProgressBar) this.findViewById(R.id.uploadbar);  
        resulView = (TextView)this.findViewById(R.id.result);  
        Button button =(Button)this.findViewById(R.id.button);  
        button.setOnClickListener(new View.OnClickListener() {  
            @Override  
            public void onClick(View v) {  
                String filename = filenameText.getText().toString();  
                if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){  
                    File uploadFile = new File(Environment.getExternalStorageDirectory(), filename);  
                    if(uploadFile.exists()){  
                        uploadFile(uploadFile);  
                    }else{  
                        Toast.makeText(UploadActivity.this, R.string.filenotexsit, 1).show();  
                    }  
                }else{  
                    Toast.makeText(UploadActivity.this, R.string.sdcarderror, 1).show();  
                }  
            }  
        });  
    }  
    /** 
     * 上传文件 
     * @param uploadFile 
     */  
    private void uploadFile(final File uploadFile) {  
        new Thread(new Runnable() {           
            @Override  
            public void run() {  
                try {  
                    uploadbar.setMax((int)uploadFile.length());  
                    String souceid = logService.getBindId(uploadFile);  
                    String head = "Content-Length="+ uploadFile.length() + ";filename="+ uploadFile.getName() + ";sourceid="+  
                        (souceid==null? "" : souceid)+"\r\n";  
                    Socket socket = new Socket("192.168.1.123", 8787);  
                    OutputStream outStream = socket.getOutputStream();  
                    outStream.write(head.getBytes());  
                      
                    PushbackInputStream inStream = new PushbackInputStream(socket.getInputStream());      
                    String response = StreamTool.readLine(inStream);  
                    String[] items = response.split(";");  
                    String responseid = items[0].substring(items[0].indexOf("=")+1);  
                    String position = items[1].substring(items[1].indexOf("=")+1);  
                    if(souceid==null){//代表原来没有上传过此文件,往数据库添加一条绑定记录  
                        logService.save(responseid, uploadFile);  
                    }  
                    RandomAccessFile fileOutStream = new RandomAccessFile(uploadFile, "r");  
                    fileOutStream.seek(Integer.valueOf(position));  
                    byte[] buffer = new byte[1024];  
                    int len = -1;  
                    int length = Integer.valueOf(position);  
                    while( (len = fileOutStream.read(buffer)) != -1){  
                        outStream.write(buffer, 0, len);  
                        length += len;  
                        Message msg = new Message();  
                        msg.getData().putInt("size", length);  
                        handler.sendMessage(msg);  
                    }  
                    fileOutStream.close();  
                    outStream.close();  
                    inStream.close();  
                    socket.close();  
                    if(length==uploadFile.length()) logService.delete(uploadFile);  
                } catch (Exception e) {  
                    e.printStackTrace();  
                }  
            }  
        }).start();  
    }  
}
UploadLogService.java

public class UploadLogService {  
    private DBOpenHelper dbOpenHelper;  
      
    public UploadLogService(Context context){  
        this.dbOpenHelper = new DBOpenHelper(context);  
    }  
      
    public void save(String sourceid, File uploadFile){  
        SQLiteDatabase db = dbOpenHelper.getWritableDatabase();  
        db.execSQL("insert into uploadlog(uploadfilepath, sourceid) values(?,?)",  
                new Object[]{uploadFile.getAbsolutePath(),sourceid});  
    }  
      
    public void delete(File uploadFile){  
        SQLiteDatabase db = dbOpenHelper.getWritableDatabase();  
        db.execSQL("delete from uploadlog where uploadfilepath=?", new Object[]{uploadFile.getAbsolutePath()});  
    }  
      
    public String getBindId(File uploadFile){  
        SQLiteDatabase db = dbOpenHelper.getReadableDatabase();  
        Cursor cursor = db.rawQuery("select sourceid from uploadlog where uploadfilepath=?",   
                new String[]{uploadFile.getAbsolutePath()});  
        if(cursor.moveToFirst()){  
            return cursor.getString(0);  
        }  
        return null;  
    }  
}
DBOpenHelper.java

public class DBOpenHelper extends SQLiteOpenHelper {  
  
    public DBOpenHelper(Context context) {  
        super(context, "upload.db", null, 1);  
    }  
  
    @Override  
    public void onCreate(SQLiteDatabase db) {  
        db.execSQL("CREATE TABLE uploadlog (_id integer primary key autoincrement, uploadfilepath varchar(100), sourceid varchar(10))");  
    }  
  
    @Override  
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {  
        db.execSQL("DROP TABLE IF EXISTS uploadlog");  
        onCreate(db);         
    }  
  
}
StreamTool.java同服务端

关于上传文件可以考虑使用mina框架








	public SocketThread(){
		count++;
	}
 
  
 
  
 
  
 
 

你可能感兴趣的:(java-socket,java,socket,spring,android)