Java: 用socket进行多文件连续传输

本文探讨了使用Socket在Android中高效传输多个文件的策略,解决了传统方法中的效率和兼容性问题,提出了一种新的文件名和文件类型传输方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近在写Android的时候用到socket来传输多个文件,在网上找了不少方案,自己也试了一些,发现大多数方案存在一些问题。

这些问题是:

1. 每传输一个新的文件都要关闭旧的socket建一个新的socket,这样效率不高,也会产生一系列bug。能不能在一个socket连接里面把所有文件传完?

2. 很多方案并没有传输文件名,而是采取在接收端直接按照预知的文件类型创建随机的文件名。这样的好处是不用单独处理文件名的传输,但带来了兼容性的问题。比如在传输图片的时候,不止有jpg、png的文件类型,可能还有3gp等更多的文件类型,此时如果他们的后缀不能互换,假如发送端是3gp接收端直接保存为jpg,会不会使保存的文件无法打开?还有,同一个socket链接也不能同时处理不能类型的文件,比如我要同时传送图片和音频,这种方案显然也是不行的。所以我认为为了兼容性,文件名还是要传的。

3. 还有就是文件名何时传输的问题。是在一开始先把所有文件名先传过去,还是传一个文件名加一个文件,再传一个文件加一个文件,以此类推。我觉得这个是无所谓的,看个人选择。

最终方案选择:

我最后定下来的就是,先传所有文件总类型和文件数量。文件总类型指的是,举个例子,比如这次传输是只传了图片还是图片和音频混着来。文件数量顾名思义,这次一共要传多少个文件。

然后分别传每个文件。每个文件的传输分为文件头信息和文件本身。文件头信息包括“start”标签和文件名和文件长度。

下面来看具体的代码:

首先是发送端:

连接的建立就不赘述了,每个程序情况都不一样,不清楚的按问题谷歌/百度,这篇文章的重点不在此。直接看如何发送。

//所有的文件名都放在list里面
String[] fileNames = intent.getStringArrayExtra("FILE_LIST");

//dos是输出流,这一行放在这里怕看代码的人看不到引起误会
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());

//写入文件总类型和文件数
dos.writeInt(requestCode);
dos.writeInt(fileNames.length);

File file;

//每一个循环就会发送一个文件
for (int i=0; i<fileNames.length; i++){

        ...
        //省略过程,到这一步你要得到需要的文件实例和文件输出流和文件长度
        file = new File(filePath);
        fileLength = (int)file.length();
        FileInputStream fis = (FileInputStream)is;

        //写入文件头信息,要保证头文件信息长度一致!!记得规范一下头文件格式
        String fileMessage = String.format("Start--%-128s--%012d",file.getName(),file.length());
        dos.write(fileMessage.getBytes(),0,fileMessage.getBytes().length);
        dos.flush();

        //写入文件信息
        byte[] bytes = new byte[1024];
        int length = 0;
        long progress = 0;

        while((length = fis.read(bytes, 0, bytes.length)) != -1) {
        if (length!=1024) System.out.println(length);
        dos.write(bytes, 0, length);
        dos.flush();
        progress += length;

        if(fis != null)
        fis.close();
        }
}

发送端没什么好说的,应该都看得懂。下面说重点接收端。

接收端代码:

接收端的代码是重点。重点在前一个文件的末尾和后一个文件的头文件不能搞乱。因为文件大小一般不会是1024的整数倍,所以如果bytes[]一直是1024的话,可能会把前一个文件的末尾和后一个文件的前部分一起接收。你收到的头文件就会是乱码的。所以要及时调整bytes[]的大小。让文件接收的大小和文件大小对上。具体的逻辑如代码所示。

//获取输入流,没什么好说的
dis = new DataInputStream(client.getInputStream());

//读取文件总类型和文件数
int requestCode = dis.readInt();
int numOfFiles = dis.readInt();

//每一个循环读取一个文件
for (int i=0; i<numOfFiles; i++){

        //读取文件头信息
        byte[] fileMessageByte = new byte[149];
        int headerLength = dis.read(fileMessageByte,0,fileMessageByte.length);
        String fileMessage = new String(fileMessageByte);

        //parse文件头信息,得到文件名称和文件长度
        String fileNameWithSpace = fileMessage.split("--")[1];
        String fileName = fileNameWithSpace.split(" ")[0];
        long fileLength = Long.parseLong(fileMessage.split("--")[2]);

        //创建文件实例和文件输出流
        File file = new File(context.getExternalFilesDir("received"),
        System.currentTimeMillis()+"-"+fileName);
        FileOutputStream fos = null;
        fos = new FileOutputStream(file);

        //开始接受文件主体
        byte[] bytes = new byte[1024];
        int length = 0;
        long progress = 0;
        
        //这个地方是关键,你输出的总byte必须和文件长度是一样的,所以当剩余的文件长度小于传输
        //的bytes[]的长度的话,要把bytes[]的长度重新设置为小于等于剩余文件的长度。当fileLength
        //为0的时候,所有数据传输完毕。
        while(((length = dis.read(bytes, 0, bytes.length)) != -1)) {

            fos.write(bytes, 0, length);
            fos.flush();
            progress += length;
            fileLength -= length;

            if (fileLength == 0){
                break;
            }
            if (fileLength < bytes.length){
                bytes = new byte[(int)fileLength];
            }
        }

        if(fos != null) {
        fos.close();
        }
}

我的项目地址为https://github.com/no-10/WiFiDirect

具体的代码位置

接收端在:https://github.com/no-10/WiFiDirect/blob/master/app/src/main/java/com/example/android/wifidirect/DeviceDetailFragment.java

发送端在:https://github.com/no-10/WiFiDirect/blob/master/app/src/main/java/com/example/android/wifidirect/FileTransferService.java

想在实战项目中参考的可以参考一下。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值