谨以此文记录曾经的硝烟,如果还能给大家带来一丢丢的帮助,那是再好不过的事情。
任务要求:1、请求需要下载数据的总大小;
2、使用Socket从服务器分段下载字节流数据(byte[]数据);
3、以byte[]的形式追加保存到.txt文件中。
一、请求数据大小
这里的Socket、OutputStream、InputStream声明的全局,因为业务需求"请求数据的大小"和"下载数据"要一气呵成,即请求完数据大小之后,需要在短时间内用同一个Socket链接立马请求下载数据,如果间隔时间太长(大概几十秒)或再次new Socket的话,就请求不到任何数据了。
private Socket socket;
private OutputStream socketWriter;
private InputStream socketReader;
private void socketConnect(final String sizeStr, final String downStr, final String nameStr) {
new Thread(new Runnable() {
@Override
public void run() {
try {
socket = new Socket(Constants.CONTROLLER_IP, Constants.PORT_TCP);
getFileSize(sizeStr, downStr, nameStr);
} catch (IOException e) {
e.printStackTrace();
udpSend();
}
}
}).start();
}
/**
* 请求文件大小
* sizeStr:请求文件大小所需参数
* downStr:文件下载所需参数
* nameStr:保存文件所需参数
*/
private void getFileSize(String sizeStr, String downStr, String nameStr) {
try {
//向服务器端发送消息
socketWriter = socket.getOutputStream();
socketWriter.write(OrderUtils.hexStringToBytes(sizeStr)); //业务需求传递参数为16进制字节码
socketWriter.flush();
//接收来自服务器端的消息
socketReader = socket.getInputStream();
byte[] b = new byte[16]; //服务器返回数据长度固定为16字节
socketReader.read(b);
byte[] cBytes = new byte[4]; //创建4个字节码的字节数组
System.arraycopy(b, 12, cBytes, 0, 4); //将返回值里的文件大小提炼出来(返回数据的最后四个字节为数据大小)
long size = OrderUtils.getDLong(cBytes); //获取文件实际大小
int indexSize = 6 * 1024; //设置每次请求的字节大小(根据自己的需求设置)
int offsetIndexSize; //偏移量的字节大小
int num = (int) (size / indexSize); //循环请求次数
byte[] indexByte = new byte[4]; //偏移量,需要累加(以4字节的长度保存偏移量,并且需要进行高低位反转)
if (num > 1) { //需要多次循环追加请求
int left = (int) (size % indexSize); //获取最后余下的数据大小,最后请求
for (int i = 0; i < num; i++) {
offsetIndexSize = i * indexSize; //偏移量累加
indexByte = OrderUtils.getHbyte(BigInteger.valueOf(offsetIndexSize)); //将偏移量设置为16进制数组并进行反转
downloadFile(indexSize, indexByte, 0, downStr, nameStr);
}
indexByte = OrderUtils.getHbyte(BigInteger.valueOf(indexSize * num)); //计算最后的偏移量
downloadFile(left, indexByte, 0, downStr, nameStr);
} else {
downloadFile((int) size, indexByte, 0, downStr, nameStr);
}
//关闭流
socketWriter.close();
socketReader.close();
//关闭Socket
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
二、使用Socket从服务器分段下载字节流数据(byte[]数据);
/**
* 文件下载请求
*
* @param requestNum 每次请求的字节数
* @param offsetBytes 偏移量
*/
private void downloadFile(int requestNum, byte[] offsetBytes, int flag, String downStr, String nameStr) {
try {
socketWriter = socket.getOutputStream();
byte[] sendBytes = OrderUtils.hexStringToBytes(downStr);
System.arraycopy(offsetBytes, 0, sendBytes, 12, 4); //将偏移量填入需要发送的字节数组里
byte[] downLoadBytes = OrderUtils.getHbyte(BigInteger.valueOf(requestNum));
System.arraycopy(downLoadBytes, 0, sendBytes, 16, 4); //将此次下载量填入需要发送的字节数组里
socketWriter.write(sendBytes);
socketWriter.flush();
//接收来自服务器端的消息
socketReader = socket.getInputStream();
byte[] b1 = new byte[1024];
int len = 0;
while ((len) != -1 && sum < requestNum) {
len = socketReader.read(b1); //len的读取一定要在循环里,否则会出问题
if (flag == 0) { //每次请求下载都需要删除一次数据的包头
flag = 1;
byte[] b2 = new byte[len - 12];
System.arraycopy(b1, 12, b2, 0, len - 12);
OrderUtils.createFileWithByte(b2, nameStr, len - 12); //每读一次,就往文件里写入一次
} else {
OrderUtils.createFileWithByte(b1, nameStr, len);
}
}
} catch (IOException e) {
e.printStackTrace();
Log.e("socket1===", "===printStackTrace===" + e.toString());
}
}
==========以下是工具类里的方法==========
/**
* 将16进制字符串转化为字节数组
*
* @param hexString the hex string
* @return byte[]
*/
public static byte[] hexStringToBytes(String hexString) {
if (hexString == null || hexString.equals("")) {
return null;
}
hexString = hexString.toUpperCase();
int length = hexString.length() / 2;
char[] hexChars = hexString.toCharArray();
byte[] d = new byte[length];
for (int i = 0; i < length; i++) {
int pos = i * 2;
d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
}
return d;
}
/**
* 获取文件实际大小(10进制)
*
* @param bytes
* @return
*/
public static long getDLong(byte[] bytes) {
String s = byte2HexStr(higherAndLowerConvert(bytes)).replace(" ", "").trim();
long dataSize = Long.parseLong(s, 16);
return dataSize;
}
/**
* bytes转换成十六进制字符串
*
* @param b byte数组
* @return String 每个Byte值之间空格分隔
*/
public static String byte2HexStr(byte[] b) {
String stmp = "";
StringBuilder sb = new StringBuilder("");
for (int n = 0; n < b.length; n++) {
stmp = Integer.toHexString(b[n] & 0xFF);
sb.append((stmp.length() == 1) ? "0" + stmp : stmp);
sb.append(" ");
}
return sb.toString().toUpperCase().trim();
}
/**
* 获取偏移量(16进制)
*
* @param offsetSize 偏移量
* @return
*/
public static byte[] getHbyte(BigInteger offsetSize) {
Log.e("socket===", "===offsetSize===" + offsetSize);
byte[] offsetBytes = new byte[4];
BigInteger bigInteger = new BigInteger(offsetSize.toString(), 10);
String offsetStr = bigInteger.toString(16);
Log.e("socket===", "===下载长度===" + offsetStr);
if (offsetStr.length() % 2 == 1) {
offsetStr = "0" + offsetStr;
}
offsetBytes = hexStringToBytes(offsetStr);
return higherAndLowerConvert(offsetBytes);
}
public static byte[] higherAndLowerConvert(byte[] bytes) {
Log.e("高低位转换==前==", byte2HexStr(bytes));
byte[] newBytes = new byte[4];
if (bytes.length == 0) {
newBytes[0] = 0x00;
newBytes[1] = 0x00;
newBytes[2] = 0x00;
newBytes[3] = 0x00;
} else if (bytes.length == 1) {
newBytes[0] = bytes[0];
newBytes[1] = 0x00;
newBytes[2] = 0x00;
newBytes[3] = 0x00;
} else if (bytes.length == 2) {
newBytes[0] = bytes[1];
newBytes[1] = bytes[0];
newBytes[2] = 0x00;
newBytes[3] = 0x00;
} else if (bytes.length == 3) {
newBytes[0] = bytes[2];
newBytes[1] = bytes[1];
newBytes[2] = bytes[0];
newBytes[3] = 0x00;
} else {
newBytes[0] = bytes[3];
newBytes[1] = bytes[2];
newBytes[2] = bytes[1];
newBytes[3] = bytes[0];
}
Log.e("高低位转换==后==", byte2HexStr(newBytes));
return newBytes;
}
==========以上是工具类里的方法==========
三、以byte[]的形式追加保存到.txt文件中。
/**
* 根据byte数组生成文件
*
* @param bytes 生成文件用到的byte数组
* @param len 此次写入的长度
*/
public static void createFileWithByte(byte[] bytes, String fileName, int len) {
/**
* 创建File对象,其中包含文件所在的目录以及文件的命名
*/
File file = new File(Constants.FILE_PATH + fileName);
// 创建FileOutputStream对象
FileOutputStream outputStream = null;
// 创建BufferedOutputStream对象
BufferedOutputStream bufferedOutputStream = null;
try {
// 如果文件不存在则创建
if (!file.exists()) {
file.createNewFile();
}
// 获取FileOutputStream对象
outputStream = new FileOutputStream(file, true); //设置追加模式
// 获取BufferedOutputStream对象
bufferedOutputStream = new BufferedOutputStream(outputStream);
// 往文件所在的缓冲输出流中写byte数据
bufferedOutputStream.write(bytes, 0, len);
// 刷出缓冲输出流,该步很关键,要是不执行flush()方法,那么文件的内容是空的。
bufferedOutputStream.flush();
} catch (Exception e) {
// 打印异常信息
e.printStackTrace();
} finally {
// 关闭创建的流对象
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bufferedOutputStream != null) {
try {
bufferedOutputStream.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
}
到这里,如果顺利的话,就已经完成了字节流数据的下载保存工作,下一篇,我将会介绍如果从文件里读取字节流数据。
扫描二维码关注公众号,回复:
11570195 查看本文章