You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python服务器向Android客户端传输SQLite数据库文件时损坏

解决SQLite数据库传输后“disk image is malformed”的问题

看起来你遇到的是很典型的文件传输不完整导致的数据库损坏问题——咱们一步步拆解问题,然后修复代码:

问题根源

你的代码里有两个核心问题直接导致了数据库损坏:

  1. Android客户端读取逻辑缺陷:你固定读取20002字节,不管实际数据库文件大小。如果文件比这个小,会写入多余的空字节;如果文件更大,只接收了部分内容,两种情况都会让SQLite识别为损坏的镜像。
  2. Python服务器缺少文件大小标识:没有先告诉客户端要接收多少数据,客户端没法判断是否已经完整接收了整个文件。

修复方案

1. 调整Python服务器代码

先发送文件的大小,再分块传输内容,既保证客户端能准确接收,也避免大文件一次性读取的内存压力:

import socket

TCP_IP = '192.168.14.87'
TCP_PORT = 9012

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((TCP_IP, TCP_PORT))
s.listen(5)
print('listening')

conn, addr = s.accept()
print("Client connected: ", addr)

filename = "trempi.db"
try:
    with open(filename, "rb") as f:
        # 先发送文件大小(用4字节整数传递,客户端可直接解析)
        f.seek(0, 2)  # 跳到文件末尾获取大小
        file_size = f.tell()
        f.seek(0)  # 回到文件开头准备读取内容
        conn.sendall(file_size.to_bytes(4, byteorder='big'))
        
        # 分块发送文件,每次4KB
        bytes_read = f.read(4096)
        while bytes_read:
            conn.sendall(bytes_read)
            bytes_read = f.read(4096)
    print("Data has been transmitted :)")
except Exception as e:
    print(f"传输出错: {e}")
finally:
    conn.close()
    s.close()

2. 修复Android客户端代码

先接收文件大小,再根据这个大小精准读取对应字节数,确保写入的文件和服务器端完全一致:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    tv = findViewById(R.id.tv1);
    btn = findViewById(R.id.btn);
    btn2 = findViewById(R.id.btn2);
    btn.setOnClickListener(this);
    btn2.setOnClickListener(this);
}

@Override
public void onClick(View v) {
    if (v == btn2) {
        Intent go = new Intent(MainActivity.this, Data.class);
        startActivity(go);
    }
    if (v == btn) {
        new Thread(new MyServer()).start();
    }
}

class MyServer implements Runnable {
    // 用主线程Looper,方便后续更新UI提示
    private Handler handler = new Handler(Looper.getMainLooper());

    @Override
    public void run() {
        Socket s = null;
        InputStream is = null;
        FileOutputStream fr = null;
        try {
            s = new Socket("192.168.14.87", 9012);
            is = s.getInputStream();
            
            // 先读取4字节的文件大小
            byte[] sizeBuffer = new byte[4];
            is.read(sizeBuffer);
            int fileSize = ByteBuffer.wrap(sizeBuffer).getInt();
            
            // 获取数据库存储路径,先删除旧文件避免混合
            File dbFile = getDatabasePath("trempi.db");
            if (dbFile.exists()) {
                dbFile.delete();
            }
            fr = new FileOutputStream(dbFile);
            
            byte[] buffer = new byte[4096];
            int bytesRead;
            int totalRead = 0;
            
            // 循环读取直到接收完所有内容
            while (totalRead < fileSize && (bytesRead = is.read(buffer, 0, Math.min(buffer.length, fileSize - totalRead))) != -1) {
                fr.write(buffer, 0, bytesRead);
                totalRead += bytesRead;
            }
            
            handler.post(() -> tv.setText("数据库传输完成!"));
        } catch (IOException e) {
            e.printStackTrace();
            handler.post(() -> tv.setText("传输出错: " + e.getMessage()));
        } finally {
            // 确保所有资源都关闭
            try {
                if (fr != null) fr.close();
                if (is != null) is.close();
                if (s != null) s.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

额外注意点

  • 如果你需要更新UI提示,一定要用主线程的Handler,避免崩溃;
  • 传输前删除旧数据库文件,防止新旧内容混合导致损坏;
  • 可以对比服务器端和客户端文件的MD5值,验证文件完整性。

内容的提问来源于stack exchange,提问作者CoffeeGod42

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 21:17:48