0
点赞
收藏
分享

微信扫一扫

Android 上传本地视频

书坊尚 08-21 06:00 阅读 14

好的,我们来创建一个简单的 Android 应用,演示如何选择一个本地视频文件并“上传”(在本例中,我们模拟上传过程,因为实际上传需要一个服务器端点)。

功能说明

  1. 选择视频: 用户点击按钮,通过系统文件选择器选择一个本地视频文件。
  2. 显示信息: 选择后,应用会显示视频文件的名称和路径(Uri)。
  3. 模拟上传: 用户点击“上传”按钮,应用会模拟上传过程(例如,显示一个进度条),并最终显示成功或失败的消息。
  4. 权限处理: 应用会请求读取外部存储的权限(对于 Android 6.0 及以上版本)。

代码实现

<!-- 文件: app/src/main/res/layout/activity_main.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="vertical"
    android:padding="16dp"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/btn_select_video"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="选择本地视频"
        android:layout_marginBottom="16dp"/>

    <TextView
        android:id="@+id/tv_file_info"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="尚未选择视频文件"
        android:textSize="16sp"
        android:background="#EEEEEE"
        android:padding="8dp"
        android:minHeight="100dp"
        android:layout_marginBottom="16dp"/>

    <Button
        android:id="@+id/btn_upload"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="上传视频"
        android:layout_marginBottom="16dp"/>

    <ProgressBar
        android:id="@+id/progress_bar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:visibility="gone"
        style="?android:attr/progressBarStyleHorizontal"/>

</LinearLayout>

<!-- 文件: app/src/main/AndroidManifest.xml -->
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <!-- 添加读取外部存储权限 -->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.VideoUpload"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

说明

  1. 权限: 应用在 AndroidManifest.xml 中声明了 READ_EXTERNAL_STORAGE 权限。在运行时(API 23+),它会检查并请求此权限。
  2. 选择视频:
  • 点击“选择本地视频”按钮会触发 checkPermissionAndPickVideo() 方法。
  • 如果有权限,openVideoPicker() 会启动一个 Intent.ACTION_PICK,并指定 MediaStore.Video.Media.EXTERNAL_CONTENT_URI 和 MIME 类型 video/*,以打开系统视频选择器。
  • 用户选择视频后,onActivityResult() 会被调用,从中可以获取到所选视频的 Uri
  1. 显示信息: displayVideoInfo() 方法利用 Uri 查询视频文件名,并将文件名和 Uri 字符串显示在 TextView 中。
  2. 模拟上传:
  • 点击“上传视频”按钮会调用 uploadVideo() 方法。
  • 该方法首先禁用按钮并显示进度条。
  • 然后使用 ExecutorService 启动一个后台线程。
  • 在后台线程中,使用 Thread.sleep(3000) 模拟一个耗时的上传过程。
  • 上传“完成”后(模拟),使用 runOnUiThread() 切换回主线程来更新 UI(隐藏进度条,启用按钮,显示 Toast 消息)。
  1. 资源管理: 在 onDestroy() 中关闭了 ExecutorService 以避免潜在的内存泄漏。

注意:

  • 真实上传: 此示例仅模拟了上传过程。要实现真正的上传,你需要将 selectedVideoUri 指向的文件内容(可能需要通过 ContentResolverInputStream 读取)发送到你的服务器 API 端点,通常使用 OkHttpRetrofit 等网络库。
  • Uri vs 文件路径: 现代 Android 中,出于安全考虑,通常返回的是 content:// 类型的 Uri,而不是直接的文件路径 (/sdcard/...)。直接使用 UriContentResolver 交互是更推荐的做法。

// 文件: app/src/main/java/com/example/videoupload/MainActivity.java
package com.example.videoupload;

import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import java.io.File;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MainActivity extends AppCompatActivity {

    private static final int REQUEST_CODE_PICK_VIDEO = 1001;
    private static final int REQUEST_CODE_PERMISSION = 1002;
    private static final String TAG = "VideoUpload";

    private Button btnSelectVideo;
    private Button btnUpload;
    private TextView tvFileInfo;
    private ProgressBar progressBar;

    private Uri selectedVideoUri;
    private ExecutorService executorService;

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

        btnSelectVideo = findViewById(R.id.btn_select_video);
        btnUpload = findViewById(R.id.btn_upload);
        tvFileInfo = findViewById(R.id.tv_file_info);
        progressBar = findViewById(R.id.progress_bar);

        btnUpload.setEnabled(false); // 初始时上传按钮不可用
        progressBar.setVisibility(View.GONE); // 初始时进度条隐藏

        executorService = Executors.newSingleThreadExecutor();

        btnSelectVideo.setOnClickListener(v -> checkPermissionAndPickVideo());
        btnUpload.setOnClickListener(v -> uploadVideo());
    }

    private void checkPermissionAndPickVideo() {
        // 检查是否有读取存储权限
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED) {
            // 如果没有权限,则请求权限
            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
                    REQUEST_CODE_PERMISSION);
        } else {
            // 如果已有权限,则打开文件选择器
            openVideoPicker();
        }
    }

    private void openVideoPicker() {
        Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Video.Media.EXTERNAL_CONTENT_URI);
        // 限制只选择视频类型
        intent.setType("video/*");
        if (intent.resolveActivity(getPackageManager()) != null) {
            startActivityForResult(intent, REQUEST_CODE_PICK_VIDEO);
        } else {
            Toast.makeText(this, "没有找到可用的文件管理器", Toast.LENGTH_SHORT).show();
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == REQUEST_CODE_PERMISSION) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // 权限被授予
                openVideoPicker();
            } else {
                // 权限被拒绝
                Toast.makeText(this, "需要存储权限才能选择视频", Toast.LENGTH_SHORT).show();
            }
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == REQUEST_CODE_PICK_VIDEO && resultCode == RESULT_OK && data != null) {
            selectedVideoUri = data.getData();
            if (selectedVideoUri != null) {
                // 显示文件信息
                displayVideoInfo(selectedVideoUri);
                btnUpload.setEnabled(true);
            }
        }
    }

    private void displayVideoInfo(Uri videoUri) {
        String fileName = getFileName(videoUri);
        String filePath = videoUri.toString(); // 注意:这通常是一个 content:// URI, 不是真实文件路径
        tvFileInfo.setText("已选择视频:\n名称: " + fileName + "\nURI: " + filePath);
    }

    // 通过 URI 获取文件名的辅助方法
    private String getFileName(Uri uri) {
        String fileName = "未知文件";
        if (uri.getScheme().equals("content")) {
            Cursor cursor = getContentResolver().query(uri, null, null, null, null);
            if (cursor != null) {
                try {
                    if (cursor.moveToFirst()) {
                        fileName = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DISPLAY_NAME));
                    }
                } finally {
                    cursor.close();
                }
            }
        } else if (uri.getScheme().equals("file")) {
            fileName = new File(uri.getPath()).getName();
        }
        return fileName;
    }

    private void uploadVideo() {
        if (selectedVideoUri == null) {
            Toast.makeText(this, "请先选择一个视频文件", Toast.LENGTH_SHORT).show();
            return;
        }

        // 禁用按钮,显示进度条
        btnSelectVideo.setEnabled(false);
        btnUpload.setEnabled(false);
        progressBar.setVisibility(View.VISIBLE);

        // 在后台线程模拟上传
        executorService.execute(() -> {
            // 模拟网络延迟
            try {
                Thread.sleep(3000); // 模拟上传耗时 3 秒
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }

            // 模拟上传成功或失败 (这里总是成功)
            boolean uploadSuccess = true;

            // 切换回主线程更新UI
            runOnUiThread(() -> {
                progressBar.setVisibility(View.GONE);
                btnSelectVideo.setEnabled(true);
                btnUpload.setEnabled(true);

                if (uploadSuccess) {
                    Toast.makeText(MainActivity.this, "视频上传成功!", Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(MainActivity.this, "视频上传失败,请重试。", Toast.LENGTH_SHORT).show();
                }
            });
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (executorService != null) {
            executorService.shutdown();
        }
    }
}



举报

相关推荐

0 条评论