android Socket文件传输(readFully)

最近写的项目里要用到文件传输的功能,因此我查找了很多相关的博文,查找到的文章都大同小异。

他们的demo能运行吗?也能运行,但是仅限于一些小文件,比如一个几百K到的txt文档,但是我们实际需要传输的数据量远大于这个数,就我们平时正常录个小视频,都要几十M的大小,因此要真应用那些方法,解决不了实际问题。

那接下来,这篇博文将用readfully方法实现较大文件的传输,当然这个也有一定的局限性,只适用一些较大的文件,大约在100M以内,对于更大的文件,涉及断点续传的问题,本篇文章没有展现。

由于我也是初学android,做一个app也是在一边学更多的知识,一边写这个项目,但是这个功能折磨了我很多天,如果这个功能实现不了,后面的工作都无法进行。

我之前应用其他人的方法放在我的项目里,要传输一个几十M的视频文件,一直报一个叫"Broken pipe"的错误,我觉得是因为传输的数据量过大导致信道终止。但是一次偶然查东西我发现了问题在哪,是接收信息端发生了问题,导致传输出现异常情况,我查阅的大量博文或资料里,通通都用read()方法来读取发送端传来的数据,但是read()方法只是将字节流中的数据读完,用read()去读,可能还没有读完就返回了,导致一些数据不能被正常读取,这个时候应用readFuly就能很好的解决这个问题。

(如果不用readFully的话用readByte方法也可以,但是在实测中,我传了一个不到一分钟的视频,大约30M,发过去用了7分钟,效率特别低。)

关于read和readFully区别可以看这个:https://blog.csdn.net/yangzhihello/article/details/8078684.

做成的效果是这样的:

server:

在这里插入图片描述

client:

在这里插入图片描述
下面放一下demo的代码,为了更直接的展现这个功能,demo中只写了sever端到client端传输数据这个的功能,至于优化等代码可以自行添加。

MainActivity:

public class MainActivity extends AppCompatActivity {

    private Button client;
    private Button server;
    static TextView filename;
    static TextView progress;
    static int vis;
    File file;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        client=findViewById(R.id.btn_client);
        server=findViewById(R.id.btn_server);
        progress=findViewById(R.id.text_progress);
        filename=findViewById(R.id.text_file);
        /*在实测时发现文件读取时android 8.1版本的手机好像不能用Environment.getExternalStorageDirectory()
        读取路径下的文件,因此我们提前要把手机android版本号获取到,然后对此进行不同的操作*/
        char Vision[] = null;
        Vision = Build.VERSION.RELEASE.toCharArray();
        //获取android版本号
        for (int i = 0, j = 1; (i < Vision.length) && (Vision[i] != '.'); i++, j = j * 10) {
            vis = vis * j + ((int) Vision[i] - 48);
        }
        //动态获取权限
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},1);
        }
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},1);
        }
        //根据版本进行不同操作来获取文件
        //文件名称和路径一定要设对,不然后面的操作都无法进行
        if(vis>9){
            file=new File(Environment.getExternalStorageDirectory().getPath()+"/test123/1.mp4");
        }
        else{
            file=new File("sdcard//storage/emulated/0/test123/"+"1.mp4");
        }
        client.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //网络等耗时操作不能在主线程中进行,因此开辟一个新的线程,进行网络传输数据的操作
                new Thread(){
                    @Override
                    public void run() {
                        new Client();
                        filename.setText("文件("+Client.name+")接收完毕");
                    }
                }.start();
            }
        });
        server.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Server(file).start();
            }
        });
    }
}

Server:

public class Server extends Thread{
    private Socket socket;
    private ServerSocket server;
    private FileInputStream fis;
    private DataOutputStream dos;
    private File file;
    private byte[] bytes;

    Server(File file){
        this.file=file;
    }

    public void run() {
        try {
            server = new ServerSocket(9999);
            while (true) {
                socket = server.accept();
                MainActivity.progress.setText("发送中");
                dos = new DataOutputStream(socket.getOutputStream());
                fis = new FileInputStream(file);
                bytes = new byte[(int)file.length()];
                //传输文件名称
                dos.writeUTF(file.getName());
                dos.flush();
                //传输文件长度
                dos.writeLong((int)file.length());
                dos.flush();
                int len=-1;
                // 将文件读取到字节数组
                while ((len = fis.read(bytes)) != -1) {
                    // 把字节数组输出
                    dos.write(bytes);
                }
                dos.flush();
                fis.close();
                dos.close();
                socket.close();
                MainActivity.progress.setText(100 + "%");
                MainActivity.filename.setText("文件("+file.getName()+")发送完毕");
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Client:

public class Client{
    private DataOutputStream dos;
    private DataInputStream dis;
    private Socket socket;
    static String name;

    Client(){
        try {
            //server端手机的ip地址
            socket = new Socket("192.168.2.170", 9999);
            dos = new DataOutputStream(socket.getOutputStream());
            dis = new DataInputStream(socket.getInputStream());
            name = dis.readUTF();// 文件名读取
            long lengths = dis.readLong();
            byte[] bt = new byte[(int) lengths];
            long p = 0;
            for (int i = 0; i < bt.length; i = i + 2 * 1024 * 1024, p = p + 2 * 1024 * 1024) {
                //每次接收2*1024*1024字节的数据,接收到最后不足2*1024*1024字节时,将剩余数据全部接收
                if (i + 2 * 1024 * 1024 > bt.length) {
                    dis.readFully(bt, i, bt.length - i);
                    //将传输的完成情况显示出来
                    MainActivity.progress.setText(String.valueOf(p * 100 / bt.length) + "%");
                    System.out.println(p * 100 / bt.length + "%");
                } else {
                    dis.readFully(bt, i, 2 * 1024 * 1024);
                    //将传输的完成情况显示出来
                    MainActivity.progress.setText(String.valueOf(p * 100 / bt.length) + "%");
                    System.out.println(p * 100 / bt.length + "%");
                }
            }
            //接收发送过来的文件
            File file;
            if (MainActivity.vis > 9) {
                file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/MySocket/" + name);
            } else {
                file = new File("sdcard//" + "MySocket/" + name);
            }
            //文件夹或文件不存在就创建
            if (!file.exists()) {
                file.getParentFile().mkdirs();
                file.createNewFile();
            }
            //将字节数组中的数据写入本地已创建好的文件里
            FileOutputStream fops = new FileOutputStream(file.getAbsoluteFile());
            fops.write(bt);
            fops.flush();
            fops.close();
            dis.close();
            dos.close();
            socket.close();
            System.out.println("文件接收完毕。");
            MainActivity.progress.setText( "100%");
        } catch(IOException e){
            e.printStackTrace();
        }
    }
}

xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/text_file"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:layout_above="@+id/btn_server"/>

    <TextView
        android:id="@+id/text_progress"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="30sp"
        android:text="0%"
        android:gravity="center"
        android:layout_centerInParent="true"/>



    <Button
        android:id="@+id/btn_server"
        android:layout_width="300dp"
        android:layout_height="wrap_content"
        android:text="发送文件"
        android:layout_centerHorizontal="true"
        android:layout_above="@+id/btn_client"/>

    <Button
        android:id="@+id/btn_client"
        android:layout_width="300dp"
        android:layout_height="wrap_content"
        android:text="接收文件"
        android:layout_centerHorizontal="true"
        android:layout_alignParentBottom="true"
        android:layout_marginBottom="100dp"/>

</RelativeLayout>

需要注意的几点:
1.socket通信只能在同一子网下,也就是说两个手机必须连接同一WIFI,不能说一个手机连着WIFI,一个手机用流量。
2.涉及到文件读取和联网的相关权限一定要加好
,除了权限尤其不要忘了在mainifests文件里加
android:requestLegacyExternalStorage="true"

在这里插入图片描述
这里是源码
Android Socket文件传输源码下载

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

recycle1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值