Python服务器向Android客户端传输SQLite数据库文件时损坏
解决SQLite数据库传输后“disk image is malformed”的问题
看起来你遇到的是很典型的文件传输不完整导致的数据库损坏问题——咱们一步步拆解问题,然后修复代码:
问题根源
你的代码里有两个核心问题直接导致了数据库损坏:
- Android客户端读取逻辑缺陷:你固定读取
20002字节,不管实际数据库文件大小。如果文件比这个小,会写入多余的空字节;如果文件更大,只接收了部分内容,两种情况都会让SQLite识别为损坏的镜像。 - 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
相关产品推荐
相关产品推荐

