安卓间通过Socket在局域网传输文件

声明:本文参考自网络资料,绝非首创,感谢以下,但不止于以下几篇博客:
https://www.cnblogs.com/yangfengwu/p/12543923.html
https://blog.csdn.net/cai13160674275/article/details/50152579
https://blog.csdn.net/feibafeibafeiba/article/details/53516574

1. A2ATransferActivity.Java

A2ATransferActivity.Java

public class A2ATransferActivity extends AppCompatActivity implements View.OnClickListener {
    private static final int FILE_CODE = 0;
    private static final int REQUEST_CODE = 1;
    private TextView tvMsg;
    private EditText txtIP, txtPort, txtEt;
    private Button btnSend;
    private Handler handler;
    private SocketManager socketManager;
    private final String[] PERMISSIONS_STORAGE = {
            "android.permission.READ_EXTERNAL_STORAGE",
            "android.permission.WRITE_EXTERNAL_STORAGE" };

    private final String TAG = A2ATransferActivity.this.getClass().getSimpleName();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_a2a_transfer);

        tvMsg = (TextView) findViewById(R.id.tvMsg);
        txtIP = (EditText) findViewById(R.id.txtIP);
        txtPort = (EditText) findViewById(R.id.txtPort);
        txtEt = (EditText) findViewById(R.id.et);
        btnSend = (Button) findViewById(R.id.btnSend);

        ActivityCompat.requestPermissions(A2ATransferActivity.this, PERMISSIONS_STORAGE, REQUEST_CODE);


        btnSend.setOnClickListener(this);

        handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case 0:
                        SimpleDateFormat format = new SimpleDateFormat("hh:mm:ss");
                        txtEt.append("\n[" + format.format(new Date()) + "]" + msg.obj.toString());
                        break;
                    case 1:
                        tvMsg.setText("本机IP:" + GetIpAddress() + " 监听端口:" + msg.obj.toString());
                        break;
                    case 2:
                        Toast.makeText(getApplicationContext(), msg.obj.toString(), Toast.LENGTH_SHORT).show();
                        break;
                    default:
                        break;
                }
            }
        };
            socketManager = new SocketManager(handler);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btnSend:
                Intent i = new Intent(A2ATransferActivity.this, FilePickerActivity.class);
                i.putExtra(FilePickerActivity.EXTRA_ALLOW_MULTIPLE, true);
                i.putExtra(FilePickerActivity.EXTRA_ALLOW_CREATE_DIR, false);
                i.putExtra(FilePickerActivity.EXTRA_MODE, FilePickerActivity.MODE_FILE);
                i.putExtra(FilePickerActivity.EXTRA_START_PATH, Environment.getExternalStorageDirectory().getPath());
                Log.i(TAG, "onClick: Environment.getExternalStorageDirectory().getPath()" + Environment.getExternalStorageDirectory().getPath());

                startActivityForResult(i, FILE_CODE);
                break;
            default:
                break;
        }
    }


    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        Log.i(TAG, "onActivityResult: + resultCode" + resultCode);
        if (requestCode == FILE_CODE && resultCode == Activity.RESULT_OK) {
            final String ipAddress = txtIP.getText().toString();
            final int port = Integer.parseInt(txtPort.getText().toString());
            if (data.getBooleanExtra(FilePickerActivity.EXTRA_ALLOW_MULTIPLE, true)) {
                // For JellyBean and above
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                    ClipData clip = data.getClipData();
                    final ArrayList<String> fileNames = new ArrayList<>();
                    final ArrayList<String> paths = new ArrayList<>();
                    if (clip != null) {
                        for (int i = 0; i < clip.getItemCount(); i++) {
                            Uri uri = clip.getItemAt(i).getUri();
                            paths.add(uri.getPath());
                            fileNames.add(uri.getLastPathSegment());
                        }
                        Message.obtain(handler, 0, "正在发送至" + ipAddress + ":" + port).sendToTarget();
                        Thread sendThread = new Thread(new Runnable() {
                            @Override
                            public void run() {
                                Log.i(TAG, "run:fileNames.size() =  " + fileNames.size());
                                socketManager.SendFile(fileNames, paths, ipAddress, port);
                            }
                        });
                        sendThread.start();
                    }
                } else {
                    final ArrayList<String> paths = data.getStringArrayListExtra
                            (FilePickerActivity.EXTRA_PATHS);
                    final ArrayList<String> fileNames = new ArrayList<>();
                    if (paths != null) {
                        for (String path : paths) {
                            Uri uri = Uri.parse(path);
                            paths.add(uri.getPath());
                            fileNames.add(uri.getLastPathSegment());
                            socketManager.SendFile(fileNames, paths, ipAddress, port);
                        }
                        Message.obtain(handler, 0, "正在发送至" + ipAddress + ":" + port).sendToTarget();
                        Thread sendThread = new Thread(new Runnable() {
                            @Override
                            public void run() {
                                socketManager.SendFile(fileNames, paths, ipAddress, port);
                            }
                        });
                        sendThread.start();
                    }
                }
            }
        }
        super.onActivityResult(requestCode, resultCode, data);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //System.exit(0);
    }

    public String GetIpAddress() {
        WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(WIFI_SERVICE);
        WifiInfo wifiInfo = wifiManager.getConnectionInfo();
        int i = wifiInfo.getIpAddress();
        return (i & 0xFF) + "." +
                ((i >> 8) & 0xFF) + "." +
                ((i >> 16) & 0xFF) + "." +
                ((i >> 24) & 0xFF);
    }
}

2. SocketManager

SocketManager

public class SocketManager {
    private ServerSocket ss;
    private Handler handler = null;
    private final String TAG = SocketManager.this.getClass().getSimpleName();
    private final String regEx = "/root/";


    public SocketManager(Handler handler) {
        this.handler = handler;
        int port = 9999;
        while (port > 9000) {
            try {
                //1. 建立连接监听窗口;
                ss = new ServerSocket(port);

                Log.i(TAG, "server.getReuseAddress() = " + ss.getReuseAddress());
                if (!ss.getReuseAddress()) {
                    ss.setReuseAddress(true);
                }
                break;
            } catch (Exception e) {
                port--;
                Log.i(TAG, "SocketManager: e = " + e);
            }
        }
        SendMessage(1, port);
        Thread receiveFileThread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    Log.i(TAG, "ReceiveFile  run:    被调用了!  ");
                    ReceiveFile();
                }
            }
        });
        receiveFileThread.start();
    }

    void SendMessage(int what, Object obj) {
        if (handler != null) {
            Message.obtain(handler, what, obj).sendToTarget();
        }
    }

    public void SendFile(ArrayList<String> fileName, ArrayList<String> path, String ipAddress, int port) {
        Log.i(TAG, "SendFile:fileName(0) =  " + fileName.get(0).toString());
        Log.i(TAG, "SendFile:path(0) =  " + path.get(0).toString());

        try {
            Socket name = new Socket(ipAddress, port);
            Log.i(TAG, "SendFile: Socketname =  " + name);
            for (int i = 0; i < fileName.size(); i++) {

                OutputStream outputName = name.getOutputStream();
                OutputStreamWriter outputWriter = new OutputStreamWriter(outputName);
                BufferedWriter bwName = new BufferedWriter(outputWriter);
                bwName.write(fileName.get(i));
                bwName.close();
                outputWriter.close();
                outputName.close();
                name.close();
                SendMessage(0, "正在发送" + fileName.get(i));

                Socket data = new Socket(ipAddress, port);
                Log.i(TAG, "SendFile: Socketdata =  " + data);
                OutputStream outputData = data.getOutputStream();
                Log.i(TAG, "outputData = " + outputData);

                Log.i(TAG, "path.get(i)  = " + path.get(i));

                //File file = new File(path.get(i));
                //以下这行爆出:文件不存在;原因在于默认得到的目录是 带有 /root/前缀的;故输入流无法创建成功;
                String tempPath = path.get(i).replaceAll(regEx, "");
                FileInputStream fileInput = new FileInputStream(tempPath);
                Log.i(TAG, "fileInput = " + fileInput);
                //判断是否读到文件末尾
                int size = -1;
                byte[] buffer = new byte[1024];
                Log.i(TAG, "SendFile: size =  " + size);
                while ((size = fileInput.read(buffer)) > 0) {
                    Log.i(TAG, "SendFile: While : size =  " + size);
                    outputData.write(buffer, 0, size);
                    outputData.flush();
                }
                //告诉服务端,文件已传输完毕
                //data.shutdownOutput();
                Log.i(TAG, "SendFile: While : 完成 " + size);

                outputData.close();
                fileInput.close();
                data.close();
                SendMessage(0, fileName.get(i) + "  发送完成");
                SendMessage(0, "所有文件发送完成");

            }


        } catch (Exception e) {
            Log.i(TAG, "发送错误   +   " + e.getMessage());
            SendMessage(0, "发送错误:\n" + e.getMessage());
        }
    }

    void ReceiveFile() {
        Log.i(TAG, "ReceiveFile:    被用调了!  ");
        try {
            //2. 连接客户端对象
            //阻塞式方法,只有客户端连接了才会继续往下运行
            while (true) {
                Socket name = ss.accept();
                Log.i(TAG, "ReceiveFile  Socket name =   " + name);
                //3.获取客户端发来的数据
                InputStream nameIPS = name.getInputStream();
                Log.i(TAG, "ReceiveFile  :nameIPS  =  " + nameIPS);
                InputStreamReader inputStreamReader = new InputStreamReader(nameIPS);
                Log.i(TAG, "ReceiveFile  inputStreamReader  =  " + inputStreamReader);
                //4.开始读取,获取输入信息
                BufferedReader br = new BufferedReader(inputStreamReader);

                String fileName = br.readLine();
                Log.i(TAG, "ReceiveFile  br  =  " + br + "  fileName = br.readLine() = " + br.readLine());

                br.close();
                inputStreamReader.close();
                nameIPS.close();
                name.close();
                SendMessage(0, "正在接收:" + fileName);

                Socket data = ss.accept();
                Log.i(TAG, "ReceiveFile  Socket data =   " + data);
                //3. 获取客户端发送过来的数据
                InputStream dataIPS = data.getInputStream();
                Log.i(TAG, "ReceiveFile:  dataIPS  =  " + dataIPS);
                //BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(dataIPS));

                String savePath = Environment.getExternalStorageDirectory().getPath() + "/DCIM/Screenshots/" + fileName;
                savePath = savePath.replaceAll(regEx, "");
                Log.i(TAG, "ReceiveFile  savePath  =  " + savePath);

                FileOutputStream fos = new FileOutputStream(savePath, false);
                Log.i(TAG, "ReceiveFile FileOutputStream fos   =  " + fos);
                //装载文件名的数组
                byte[] buffer = new byte[1024];
                int size;
                while ((size = dataIPS.read(buffer)) != -1) {
                    Log.i(TAG, "ReceiveFile size  ==  " + size);
                    fos.write(buffer, 0, size);
                    fos.flush();
                }
                fos.flush();
                fos.close();
                //告诉发送端我已经接收完毕
                //OutputStream outputStream = data.getOutputStream();
                fos.close();
                dataIPS.close();
                data.close();
                SendMessage(0, fileName + "接收完成");
            }
        } catch (Exception e) {
            SendMessage(0, "接收错误:\n" + e.getMessage());
        }
    }
}

3. activity_a2a_transfer.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="16dp"
    android:paddingTop="16dp"
    android:paddingRight="16dp"
    android:paddingBottom="16dp">

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <TextView
                android:id="@+id/tvMsg"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_alignParentTop="true"
                android:focusable="false"
                android:focusableInTouchMode="false"
                android:text="TextView"
                android:textColor="#AAA" />

            <EditText
                android:id="@+id/txtIP"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_below="@+id/tvMsg"
                android:layout_centerVertical="true"
                android:contentDescription="目标IP地址"
                android:ems="10"
                android:text="192.168.1.10" />

            <EditText
                android:id="@+id/txtPort"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_below="@+id/txtIP"
                android:layout_alignLeft="@+id/txtIP"
                android:ems="10"
                android:text="9999" />

            <EditText
                android:id="@+id/et"
                android:layout_width="match_parent"
                android:layout_height="300dp"
                android:layout_below="@+id/txtPort"
                android:layout_alignLeft="@+id/txtIP"
                android:clickable="false"
                android:editable="false"
                android:ems="10"
                android:focusable="false"
                android:focusableInTouchMode="false"
                android:gravity="center_vertical|left|top"
                android:inputType="textMultiLine"
                android:longClickable="false"
                android:scrollbarAlwaysDrawVerticalTrack="true"
                android:scrollbarStyle="insideInset"
                android:scrollbars="vertical"
                android:textSize="15dp">

                <requestFocus />
            </EditText>

            <Button
                android:id="@+id/btnSend"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_below="@+id/et"
                android:layout_alignLeft="@+id/txtPort"
                android:text="选择文件并发送" />



            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textColor="#090909"
                android:textSize="16sp"
                android:text="注意:\n本机IP应在WIFI设置中设置为静态!\n文件默认保存在手机截图的默认保存位置\n输入框输入接收对象的IP;"
                android:layout_below="@+id/btnSend"/>


        </RelativeLayout>
    </ScrollView>

</RelativeLayout>

4. 依赖

//使用指南:https://github.com/spacecowboy/NoNonsense-FilePicker
implementation 'com.nononsenseapps:filepicker:4.1.0'

5. manifest

  1. 权限

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission
            android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"
            tools:ignore="ProtectedPermissions" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
    
  2. 在 application 中添加如下:

    <activity
        android:name="com.nononsenseapps.filepicker.FilePickerActivity"
        android:label="选择文件"
        android:theme="@style/FilePickerTheme">
        <intent-filter>
            <action android:name="android.intent.action.GET_CONTENT" />
            <category android:name="android.intent.category.DEFAULT" />
        </intent-filter>
    </activity>
    <provider
        android:name="androidx.core.content.FileProvider"
        android:authorities="${applicationId}.provider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/nnf_provider_paths" />
    </provider>
    

6. 注意

  1. 本文没有进行权限处理,打开应用,默认请求存储权限,如果选择禁止并没有做处理;
  2. 本文接收文件默认保存的目录在:安卓系统截图的保存位置:DCIM/Scrennshots ,可以自行调试;
  3. 使用时将安卓端 IP 设置为静态 IP;
  4. 推荐不错的项目:https://www.imooc.com/article/296288;这不是我的文章,不过文中两个项目十分不错,可以借鉴。
  5. 如果可行,请给我赞把。笔芯

声明:本文整理自网络,如有侵权请联系我。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

liusaisaiV1

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

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

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

打赏作者

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

抵扣说明:

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

余额充值