前言:文件包括,图片,视频,音频和非媒体文件,如果目标文件是在服务器端,那么操作流程是:
1.先通过okhttp将文件下载下来,下载完成后得到文件对象file。
2.再通过io流将文件保存到本地,注意此操作需要放到子线程中进行。
3.保存文件的路径需要注意,如果文件不想被当前的手机用户在文件管理器中查看到,
则推荐context.getFilesDir(),具体代码如下:
//File targetDir = new File(context.getFilesDir(), "private_file");
/**
* 从缓存目录复制到应用私有持久化目录(不会被系统清理)
*/
public static File saveToPrivateFilesDir(Context context,String path) {
// 1. 源文件(你的下载文件)
File sourceFile = new File(path);//"/data/user/0/com.sportal/cache/55D0A490-EEB8-4903-9A64-05F8C6207A5F.mov"
if (!sourceFile.exists()) {
return null; // 源文件不存在
}
//context.getFilesDir(),该目录下,用户不能从文件管理器中查找到,需要root权限
// 2. 目标目录:应用私有 files 目录(持久化存储,不会被系统随意清理)
File targetDir = new File(context.getFilesDir(), "savo_file"); // 子目录:videos
if (!targetDir.exists()) {
targetDir.mkdirs(); // 创建目录
}
// 3. 目标文件(使用原文件名)
File targetFile = new File(targetDir, sourceFile.getName());
// 4. 复制文件
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream(sourceFile);
fos = new FileOutputStream(targetFile);
byte[] buffer = new byte[1024 * 4];
int len;
while ((len = fis.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
fos.flush();
return targetFile; // 保存成功,返回目标文件
} catch (IOException e) {
e.printStackTrace();
if (targetFile.exists()) {
targetFile.delete(); // 失败则删除不完整文件
}
return null;
} finally {
// 关闭流
try {
if (fis != null) fis.close();
if (fos != null) fos.close();
Log.i("xxx","下载完成");
} catch (IOException e) {
e.printStackTrace();
}
}
}
如果保存的文件想要让用户在文件管理器中查找到,则推荐公共目录,如:
//如果手机系统是Android 10+,通过 MediaStore 写入公共下载目录(无需权限)
//Android 10 以下:直接写入公共下载目录(需 WRITE_EXTERNAL_STORAGE 权限),具体代码如下:
public static Uri saveToPublicDownloadDir(Context context, String path) {
// 1. 源文件(下载的缓存文件)
File sourceFile = new File(path);
if (!sourceFile.exists()) {
Log.e("xxx", "源文件不存在");
return null;
}
// 2. 配置公共下载目录的存储信息(适配 Android 10+)
String fileName = sourceFile.getName(); // 保留原文件名(如 55D0A490...mov)
ContentResolver resolver = context.getContentResolver();
Uri targetUri = null;
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
// Android 10+:通过 MediaStore 写入公共下载目录(无需权限)
ContentValues values = new ContentValues();
values.put(MediaStore.Downloads.DISPLAY_NAME, fileName); // 文件名
values.put(MediaStore.Downloads.MIME_TYPE, getMimeType(fileName)); // 文件类型
values.put(MediaStore.Downloads.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS); // 公共下载目录
// 插入到 MediaStore 并获取 Uri
targetUri = resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, values);
if (targetUri == null) {
Log.e("xxx", "无法获取目标 Uri");
return null;
}
// 3. 将源文件数据写入目标 Uri
try (FileInputStream fis = new FileInputStream(sourceFile);
OutputStream os = resolver.openOutputStream(targetUri)) {
byte[] buffer = new byte[1024 * 4];
int len;
while ((len = fis.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
Log.i("xxx", "该文件已保存到手机");
}
} else {
// Android 10 以下:直接写入公共下载目录(需 WRITE_EXTERNAL_STORAGE 权限)
File downloadDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
File targetFile = new File(downloadDir, fileName);
// 复制文件
try (FileInputStream fis = new FileInputStream(sourceFile);
OutputStream fos = new FileOutputStream(targetFile)) {
byte[] buffer = new byte[1024 * 4];
int len;
while ((len = fis.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
// 通知系统刷新文件管理器
resolver.notifyChange(Uri.fromFile(targetFile), null);
targetUri = Uri.fromFile(targetFile);
Log.i("xxx", "文件已保存到公共下载目录");
}
}
return targetUri; // 返回保存后的 Uri(方便后续使用)
} catch (IOException e) {
e.printStackTrace();
// 失败时删除已创建的空文件
if (targetUri != null) {
resolver.delete(targetUri, null, null);
}
Log.e("xxx", "保存失败:" + e.getMessage());
return null;
}
}