Socket 分段上传文件

Socket分段上传文件思路,使用JAVA代码编写注释比较就不敲太多文字了

先上传一张传输图片完成的效果图

客户端


import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Arrays;
import java.util.Random;

/**
 * Socket 段连续传客户端
 * Dmeo调试代码
 * @version 2019年6月3日
 */
public class SectionClient {

	static OutputStream out;
	static Socket socket;
	static String path = "D:\\img\\渗透思维导图.jpg";
//	static String path = "D:\\img\\12306.jpg";
	static long fileLength;
	public static void main(String[] args) throws Exception{
		boolean isok = true;
		start(0);//一次传输完毕
		
		//分段传输测试
//		for (int i = 0; i < 5; i++) {
//			do {
//				try {
//					isok = start(5);
//				} catch (Exception e) {
//					isok = true;
//				}
//			} while (isok);
			start(0);
//			Thread.sleep(5000);
//		}
	}
	
	
	/**
	 * 上传文件到服务器
	 * @param stopNum	设置发包次数,到达发包次数关闭连接。 0不限制
	 * @return
	 * @throws Exception
	 */
	private static boolean start(int stopNum) throws Exception{
		//1.连接诶服务器
		socket = new Socket("127.0.0.1",Utils.PORT);
		System.out.println("已连接到服务器准备传送图片...");
		//2.获取输出流
		out = socket.getOutputStream();
		//3.获取输入流,
		InputStream in = socket.getInputStream();
		//发送文件信息
		sendFileInfo(path);
		
		//计算上传百分百
		byte[] bufIn = new byte[1024];
		int num = in.read(bufIn);
		int parseInt = Integer.parseInt(new String(bufIn,0,num));
		if (parseInt == 0) {
			System.out.println("已上传完成 ["+parseInt+"%]");	
		}else{
			System.out.println("已上传完成 ["+Utils.division(parseInt, fileLength)+"]");	
		}
		
		//发送文件
		sendFile(path,parseInt,stopNum);
		
		//获取上传信息
		num = in.read(bufIn);
		System.out.println(new String(bufIn,0,num));
		//关闭资源
		out.close();
		in.close();
		socket.close();
		return false;
	}
	
	private static void sendFileInfo(String path) throws IOException{
		File file = new File(path);
		if (file.exists() && file.isFile()) {
            String fileName = file.getName();
            System.out.println("文件"+fileName+"的大小是:"+file.length());
            //路径
            byte[] bytes = path.getBytes(Utils.EINGCODE);
    		out.write(getToSendData(bytes,0,bytes.length));
    		//文件大小
    		fileLength = file.length();
    		byte[] len = (""+fileLength).getBytes(Utils.EINGCODE);
            out.write(getToSendData(len,0,len.length));
        }else{
        	//非文件或文件不存在退出程序
        	System.err.println(" not file ");
    		System.exit(-1);
        }
		
		
	}
	
	/**
	 * 发送文件
	 * @param path	文件路径
	 * @param off	偏移量
	 * @throws IOException
	 * @throws InterruptedException 
	 */
	private static void sendFile(String path,int off,int num) throws IOException, InterruptedException{
		//获取图片字节流
		FileInputStream fis = new FileInputStream (path);
		//从指定的偏移量进行读取
		fis.skip(off);
		int len = 0;
		int x = 0;
		byte[] buf = new byte[1024];
		//2.往输出流里面投放数据
		while ((len = fis.read(buf)) != -1)
		{
			x++;
			out.write(getToSendData(buf,0,len));
			if (num >0) {
				if (x>num) {
					socket.close();
					fis.close();
					throw new RuntimeException("stop");
				}
			}
			int maxint = 512;
			int size = new Random().nextInt(maxint);
			while (size<=0) {
				size = new Random().nextInt(maxint);
			}
			buf = new byte[size];

		}
		//通知服务端,数据发送完毕
		socket.shutdownOutput();
		fis.close();
	}
	
	/**
	 * 转换发送的数据
	 * @param data	数据
	 * @param off	偏移量
	 * @param len	长度
	 * @return
	 * @throws IOException
	 */
	private static byte[] getToSendData(byte[] data,int off,int len) throws IOException{
		System.out.println("off:"+off+",len:"+len);
		ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
		outputStream.write(Utils.intToByteArray(len));
		outputStream.write(data,off,len);
		System.out.println(Arrays.toString(outputStream.toByteArray()));
		return outputStream.toByteArray();
	}

}

服务端


import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;

/**
 * Socket 段连续传服务端
 * Dmeo调试代码
 * @version 2019年6月3日
 */
public class SectionServer {

	
	//记录删除文件信息
	static Map<String, Info> map = new HashMap<>();
	
	//上传文件信息结构体
	static class Info{
		//文件总大小
		public long totol;
		//已上传大小
		public long len;
		//文件名称
		private String fileName;
		//读取状态
		private volatile boolean read;
		
		@Override
		public String toString() {
			return "Info [totol=" + totol + ", len=" + len + ", fileName=" + fileName + "]";
		}
	}
	
	public static void main(String[] args) throws IOException {
		ServerSocket serverSocket = new ServerSocket(Utils.PORT);
		System.err.println("Success full");
		do {
			Socket socket = serverSocket.accept();
			new Thread(newRunnable(socket)).start();
		} while (true);
	}
	
	/**
	 * 创建工作线程
	 * @param socket	连接客户端
	 * @return
	 */
	private static Runnable newRunnable(final Socket socket){
		return new Runnable() {
			//输入流
			private InputStream in;
			//输出流
			private OutputStream out;
			//数据长度占位符
			byte[] redLength = new byte[4];
			//服务存储路径
			private String path = "D:\\img\\cp\\";
			
			@Override
			public void run() {
				System.out.println("New connection accepted "+
					      socket.getInetAddress()+":"+socket.getPort());
				try {
					System.out.println("检测到客户端,准备数据接收...");
					//客户端已连接,获取输入流
					in = socket.getInputStream();
					//获取输出流,准备给客户端发送消息
					out = socket.getOutputStream();
					//获取上传文件信息
					Info info = readFileInfo();
					System.out.println(info);
					//发送文件上传文件,偏移未知
					sendData(String.valueOf(info.len));
					//接收文件
					saveFile(info);
					//上传完成返回上传文件信息
					sendData(info.toString());
				} catch (Exception e) {
//					e.printStackTrace();
				} finally{
					try {
						if (in != null) {
							in.close();							
						}
					} catch (IOException e) {
						e.printStackTrace();
					}
					try {
						if (out != null) {
							
						}
						out.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
					try {
						if (!socket.isClosed()) {
							socket.close();	
						}
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
				System.err.println("======== Close RuntimeTask  ========");
			}
			
			//读取文件信息
			private Info readFileInfo() throws IOException, InterruptedException{
				String fileName = null;
				long length = 0 ;
				if(in.read(redLength)!= -1){
					int lengthData = Utils.byteArrayToInt(redLength);
					byte[] nameBs = new byte[lengthData];
					in.read(nameBs);
					//文件名称
					fileName = getStrData(nameBs);
				}
				
				if(in.read(redLength)!= -1){
					int lengthData = Utils.byteArrayToInt(redLength);
					byte[] totalBs = new byte[lengthData];
					in.read(totalBs);
					//文件长度
					String totalStr = getStrData(totalBs);
					length = Long.valueOf(totalStr);
				}
				
				//验证是否已上传过
				if (map.containsKey(fileName)) {
					//上传过文件进行续传操作
					Info info = map.get(fileName);
					while (info.read) {
						//上一次任务还没结束,进行延迟操作否则上传文件容易出现损坏
						Thread.sleep(1000);
						System.err.println("上一次任务还没完成呢!");
					}
					if(info.len < info.totol){
						//文件没有上传完毕进行续传
						return info;							
					}else{
						//上传完成,删除已上传文件重新上传
						boolean delete = new File(path+info.fileName).delete();
						System.out.println("["+path+info.fileName+"]删除已完成文件:"+delete);
					}
				}
				
				//构造新文件结构体
				Info info = new Info();
				info.totol = length;
				info.read = false;
				info.fileName = "R_"+System.currentTimeMillis()+".jpg";
				map.put(fileName, info);
				return info;
			}
			
			/**
			 * 保存文件
			 * @param info
			 * @throws IOException
			 */
			private void saveFile(Info info) throws IOException{
				//创建图片字节流
				RandomAccessFile raf=new RandomAccessFile(path+info.fileName, "rw"); 
				System.out.println("服务已存文件大小:"+raf.length()+"|已经上传文件大小:"+info.len);
				raf.seek(info.len);	//设置保存文件偏移量
				try {
					info.read = true;//锁定读取任务
					byte[] bs = null;
					byte[] b = new byte[1];
					//往字节流里写图片数据
					while (in.read(redLength)!= -1)
					{
						//获取长度
						int lengthData = Utils.byteArrayToInt(redLength);
						if (bs == null || lengthData != bs.length) {
							bs = new byte[lengthData];							
							System.out.println("bs.length = "+bs.length+",lengthData="+lengthData);
						}
						//读取字节个数
						int read = in.read(bs);
						raf.write(bs,0,read);
						info.len = info.len+read;
						if (read < bs.length) {
							//如果读取字节数不完整,继续读取
							int size = bs.length - read;
							for (int i = 0; i < size; i++) {
								int bread = in.read(b);
								raf.write(b,0,bread);
								info.len = info.len+bread;		
							}
						}
						
					}
				} catch (Exception e) {
					System.err.println(info);
				}finally {
					info.read = false;//释放读取任务
				}
				//关闭资源
				raf.close();
			}
			
			/**
			 * 发送数据
			 * @param data
			 * @throws IOException
			 */
			private void sendData(String data) throws IOException{
				out.write(data.getBytes(Utils.EINGCODE));
			}

			/**
			 * 读取消息
			 * @param data
			 * @return
			 * @throws UnsupportedEncodingException
			 */
			private String getStrData(byte[] data) throws UnsupportedEncodingException{
				return new String(data,Utils.EINGCODE);
			}
			
//			/**
//			 * 保存文件[并发情况文件损坏]
//			 * @param info
//			 * @throws IOException
//			 */
//			@Deprecated
//			private void saveFileTwo(Info info) throws IOException{
//				//创建图片字节流
//				RandomAccessFile raf=new RandomAccessFile(path+info.fileName, "rw"); 
//				raf.seek(info.len);	
//				try {
//					byte[] bs = new byte[1];
//					//往字节流里写图片数据
//					while (in.read(redLength)!= -1)
//					{
//						int lengthData = Utils.byteArrayToInt(redLength);
//						System.out.println("new Bs bs.length = "+bs.length+",lengthData="+lengthData);
//						for (int i = 0; i < lengthData; i++) {
//							int read = in.read(bs);
//							raf.write(bs,0,read);
//							info.len = info.len+read;
//						}
//					}
//				} catch (Exception e) {
//					System.err.println(info);
//				}
//				//关闭资源
//				raf.close();
//			}
//			
//			/**
//			 * 保存文件[内存溢出]
//			 * @param info
//			 * @throws IOException
//			 */
//			@Deprecated
//			private void saveFileOne(Info info) throws IOException{
//				//创建图片字节流
//				RandomAccessFile raf=new RandomAccessFile(path+info.fileName, "rw"); 
//				raf.seek(info.len);	
//				try {
//					byte[] bs = null;
//					//往字节流里写图片数据
//					while (in.read(redLength)!= -1)
//					{
//						int lengthData = Utils.byteArrayToInt(redLength);
//						if (bs == null || bs.length != lengthData) {
//							bs = new byte[lengthData];							
//							System.out.println("bs.length = "+bs.length+",lengthData="+lengthData);
//						}
//						int read = in.read(bs);
//						raf.write(bs,0,read);
//						info.len = info.len+read;
//					}
//				} catch (Exception e) {
//					System.err.println(info);
//				}
//				//关闭资源
//				raf.close();
//			}
			
		};
		
	}
	
	

}

使用到的工具类

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.Arrays;

public class Utils {
	
	public static String EINGCODE="UTF-8";
	
	public final static int PORT = 21000;
	
	public static int byteArrayToInt(byte[] b) {  
	    return   b[3] & 0xFF |  
	            (b[2] & 0xFF) << 8 |  
	            (b[1] & 0xFF) << 16 |  
	            (b[0] & 0xFF) << 24;  
	}  
	  
	public static byte[] intToByteArray(int a) {  
	    return new byte[] {  
	        (byte) ((a >> 24) & 0xFF),  
	        (byte) ((a >> 16) & 0xFF),     
	        (byte) ((a >> 8) & 0xFF),     
	        (byte) (a & 0xFF)  
	    };  
	}  
	
	public static void main(String[] args) {
		int num = Integer.MAX_VALUE;
		byte[] intToByteArray = intToByteArray(num);
		System.out.println(Arrays.toString(intToByteArray));
		System.out.println(byteArrayToInt(intToByteArray));
	}
	
	
	/**
     * 占比计算保留小数的位数方法
     * 转成百分数
     * 当前数除以总数
     * @param  num1 ,num2  num1/num2
     * @return  rate  保留2位小数的
     */
    public static String  division(long num1,long num2){
        String rate="0.00%";
        //定义格式化起始位数
        String format="0.00";
        if(num2 != 0 && num1 != 0){
            DecimalFormat dec = new DecimalFormat(format);
            rate =  dec.format((double) num1 / num2*100)+"%";
            while(true){
                if(rate.equals(format+"%")){
                    format=format+"0";
                    DecimalFormat dec1 = new DecimalFormat(format);
                    rate =  dec1.format((double) num1 / num2*100)+"%";
                }else {
                    break;
                }
            }
        }else if(num1 != 0 && num2 == 0){
            rate = "100%";
        }
        return rate;
    }


    /**
     * 把上面得到的百分比转为字符串类型的小数  保留两位小数
     * @author  shw
     */
    public static BigDecimal perToDecimal(String percent){
        String decimal = percent.substring(0,percent.indexOf("%"));
        BigDecimal bigDecimal = new BigDecimal(decimal);
        bigDecimal.divide(new BigDecimal("100"), 4, BigDecimal.ROUND_HALF_UP);
        return bigDecimal;
    }

}

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值