Android 中使用多线程进行文件批量上传并支持暂停、续传和删除功能的代码

以下是一个 Android 中使用多线程进行文件批量上传并支持暂停、续传、删除功能且同步数据库记录的示例代码:

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Environment;
import android.util.Log;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MultiThreadBatchUploader {

    private static final String TAG = "MultiThreadBatchUploader";
    private static final int BUFFER_SIZE = 8192;
    private String uploadUrl;
    private List<UploadTaskInfo> tasksInfo;
    private ExecutorService executorService;
    private UploadDatabaseHelper databaseHelper;

    public MultiThreadBatchUploader(Context context, String uploadUrl) {
        this.uploadUrl = uploadUrl;
        tasksInfo = new ArrayList<>();
        executorService = Executors.newFixedThreadPool(3); // 可以根据实际情况调整线程数量
        databaseHelper = new UploadDatabaseHelper(context);
    }

    public void addFileForUpload(File file) {
        tasksInfo.add(new UploadTaskInfo(file));
        databaseHelper.insertUploadTask(file.getAbsolutePath(), 0);
    }

    public void startUpload() {
        for (UploadTaskInfo taskInfo : tasksInfo) {
            executorService.execute(new UploadTask(taskInfo));
        }
    }

    public void pauseUpload(int taskIndex) {
        if (taskIndex >= 0 && taskIndex < tasksInfo.size()) {
            tasksInfo.get(taskIndex).pause();
            updateDatabaseUploadProgress(taskIndex, tasksInfo.get(taskIndex).getUploadedBytes());
        }
    }

    public void resumeUpload(int taskIndex) {
        if (taskIndex >= 0 && taskIndex < tasksInfo.size()) {
            tasksInfo.get(taskIndex).resume();
            executorService.execute(new UploadTask(tasksInfo.get(taskIndex)));
            updateDatabaseUploadProgress(taskIndex, tasksInfo.get(taskIndex).getUploadedBytes());
        }
    }

    public void deleteUpload(int taskIndex) {
        if (taskIndex >= 0 && taskIndex < tasksInfo.size()) {
            tasksInfo.get(taskIndex).cancel();
            File file = tasksInfo.get(taskIndex).getFile();
            databaseHelper.deleteUploadTask(file.getAbsolutePath());
            tasksInfo.remove(taskIndex);
        }
    }

    private class UploadTaskInfo {
        private File file;
        private long uploadedBytes;
        private boolean paused;
        private boolean canceled;

        public UploadTaskInfo(File file) {
            this.file = file;
            uploadedBytes = readUploadProgress();
            paused = false;
            canceled = false;
        }

        private long readUploadProgress() {
            Cursor cursor = databaseHelper.getUploadTask(file.getAbsolutePath());
            if (cursor.moveToFirst()) {
                return cursor.getLong(cursor.getColumnIndex(UploadDatabaseHelper.COLUMN_UPLOADED_BYTES));
            }
            return 0;
        }

        private void saveUploadProgress(long bytesUploaded) {
            databaseHelper.updateUploadTask(file.getAbsolutePath(), bytesUploaded);
        }

        public void pause() {
            paused = true;
        }

        public void resume() {
            paused = false;
        }

        public void cancel() {
            canceled = true;
        }

        public boolean isPaused() {
            return paused;
        }

        public boolean isCanceled() {
            return canceled;
        }

        public long getUploadedBytes() {
            return uploadedBytes;
        }

        public File getFile() {
            return file;
        }
    }

    private class UploadTask implements Runnable {

        private UploadTaskInfo taskInfo;

        public UploadTask(UploadTaskInfo taskInfo) {
            this.taskInfo = taskInfo;
        }

        @Override
        public void run() {
            if (taskInfo.isCanceled()) {
                return;
            }
            try {
                URL url = new URL(uploadUrl);
                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                connection.setRequestMethod("POST");
                connection.setDoOutput(true);
                long fileLength = taskInfo.getFile().length();
                connection.setRequestProperty("Content-Range", "bytes " + taskInfo.getUploadedBytes() + "-" + (fileLength - 1) + "/" + fileLength);

                OutputStream outputStream = connection.getOutputStream();
                FileInputStream inputStream = new FileInputStream(taskInfo.getFile());
                inputStream.skip(taskInfo.getUploadedBytes());

                byte[] buffer = new byte[BUFFER_SIZE];
                int bytesRead;
                while ((bytesRead = inputStream.read(buffer))!= -1) {
                    if (taskInfo.isPaused()) {
                        taskInfo.saveUploadProgress(taskInfo.getUploadedBytes());
                        return;
                    }
                    outputStream.write(buffer, 0, bytesRead);
                    taskInfo.uploadedBytes += bytesRead;
                }

                inputStream.close();
                outputStream.close();

                int responseCode = connection.getResponseCode();
                if (responseCode == HttpURLConnection.HTTP_OK) {
                    Log.d(TAG, "Upload completed for file: " + taskInfo.getFile().getName());
                    taskInfo.getProgressFile().delete();
                    databaseHelper.deleteUploadTask(taskInfo.getFile().getAbsolutePath());
                } else {
                    Log.e(TAG, "Upload failed for file: " + taskInfo.getFile().getName() + " with response code: " + responseCode);
                }

            } catch (IOException e) {
                Log.e(TAG, "Upload error for file: " + taskInfo.getFile().getName() + " " + e.getMessage());
            }
        }
    }

    private static long bytesToLong(byte[] bytes) {
        return ((long) bytes[0] << 56) |
                ((long) (bytes[1] & 0xff) << 48) |
                ((long) (bytes[2] & 0xff) << 40) |
                ((long) (bytes[3] & 0xff) << 32) |
                ((long) (bytes[4] & 0xff) << 24) |
                ((long) (bytes[5] & 0xff) << 16) |
                ((long) (bytes[6] & 0xff) << 8) |
                ((long) (bytes[7] & 0xff));
    }

    private static byte[] longToBytes(long value) {
        return new byte[]{
                (byte) (value >> 56),
                (byte) (value >> 48),
                (byte) (value >> 40),
                (byte) (value >> 32),
                (byte) (value >> 24),
                (byte) (value >> 16),
                (byte) (value >> 8),
                (byte) value
        };
    }

    private void updateDatabaseUploadProgress(int taskIndex, long bytesUploaded) {
        File file = tasksInfo.get(taskIndex).getFile();
        databaseHelper.updateUploadTask(file.getAbsolutePath(), bytesUploaded);
    }

    private static class UploadDatabaseHelper extends SQLiteOpenHelper {

        private static final String DATABASE_NAME = "upload_database";
        private static final int DATABASE_VERSION = 1;
        private static final String TABLE_NAME = "upload_tasks";
        private static final String COLUMN_FILE_PATH = "file_path";
        private static final String COLUMN_UPLOADED_BYTES = "uploaded_bytes";

        public UploadDatabaseHelper(Context context) {
            super(context, DATABASE_NAME, null, DATABASE_VERSION);
        }

        @Override
        public void onCreate(SQLiteDatabase db) {
            String createTableQuery = "CREATE TABLE " + TABLE_NAME + "(" +
                    COLUMN_FILE_PATH + " TEXT PRIMARY KEY," +
                    COLUMN_UPLOADED_BYTES + " INTEGER" +
                    ")";
            db.execSQL(createTableQuery);
        }

        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            // 可以根据需要处理数据库升级
        }

        public void insertUploadTask(String filePath, long uploadedBytes) {
            SQLiteDatabase db = this.getWritableDatabase();
            ContentValues values = new ContentValues();
            values.put(COLUMN_FILE_PATH, filePath);
            values.put(COLUMN_UPLOADED_BYTES, uploadedBytes);
            db.insert(TABLE_NAME, null, values);
            db.close();
        }

        public Cursor getUploadTask(String filePath) {
            SQLiteDatabase db = this.getReadableDatabase();
            String selectQuery = "SELECT * FROM " + TABLE_NAME + " WHERE " + COLUMN_FILE_PATH + " =?";
            return db.rawQuery(selectQuery, new String[]{filePath});
        }

        public void updateUploadTask(String filePath, long uploadedBytes) {
            SQLiteDatabase db = this.getWritableDatabase();
            ContentValues values = new ContentValues();
            values.put(COLUMN_UPLOADED_BYTES, uploadedBytes);
            db.update(TABLE_NAME, values, COLUMN_FILE_PATH + " =?", new String[]{filePath});
            db.close();
        }

        public void deleteUploadTask(String filePath) {
            SQLiteDatabase db = this.getWritableDatabase();
            db.delete(TABLE_NAME, COLUMN_FILE_PATH + " =?", new String[]{filePath});
            db.close();
        }
    }

    public static void main(String[] args) {
        // 在 Android 应用中,应该从合适的上下文获取 Context 对象传递给构造函数
        Context context = null; // 这里只是示例,实际应用中应该传入正确的上下文
        MultiThreadBatchUploader uploader = new MultiThreadBatchUploader(context, "http://your-server-url.com/upload");
        File file1 = new File(Environment.getExternalStorageDirectory(), "file1.txt");
        File file2 = new File(Environment.getExternalStorageDirectory(), "file2.txt");
        uploader.addFileForUpload(file1);
        uploader.addFileForUpload(file2);
        uploader.startUpload();
    }
}

请注意以下几点:

  1. 在实际的 Android 应用中,应该从合适的上下文(如 Activity 或 Service)获取 Context 对象传递给 MultiThreadBatchUploader 的构造函数。
  2. 这个示例代码仅为演示目的,实际应用中还需要处理更多的错误情况和优化。
  3. 确保在合适的时候关闭数据库连接以避免资源泄漏。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值