Android使用Socket分包下载字节流数据并生成文件

谨以此文记录曾经的硝烟,如果还能给大家带来一丢丢的帮助,那是再好不过的事情。

任务要求: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();
            }
        }
    }
}

到这里,如果顺利的话,就已经完成了字节流数据的下载保存工作,下一篇,我将会介绍如果从文件里读取字节流数据。

猜你喜欢

转载自blog.csdn.net/S_Alics/article/details/81558075