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;
}
}