1.Flutter部分
构建一个类 用于调用原生方法 代码如下
class FileShare {
static const MethodChannel _channel = MethodChannel('YOUR_CHANNEL_NAME'); //channel名字
/// 获取分享的文件地址
static Future getOpenFileUrl() async {
var path = "";
path = await _channel.invokeMethod('getOpenFileUrl'); //方法名
return path;
}
}
2.iOS原生
2.1配置,在info.plist增加需要打开的文件类型配置,iOS文件配置参考链接:链接
...
CFBundleDocumentTypes
CFBundleTypeName
Microsoft Excel
LSHandlerRank
Alternate
LSItemContentTypes
com.microsoft.excel.xls
org.openxmlformats.spreadsheetml.sheet
2.2 原生方法获取文件地址(此处注意Flutter框架会报错,不过运行是没有问题的)
import UIKit
import Flutter
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
/// 文件路径 (iOS获取的是文件在Documents目录下的路径存放在Inbox下面)
var filePath = ""
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
///调用的方法 (channel名字与flutter统一)
let methodChannel = FlutterMethodChannel(name: "YOUR_CHANNEL_NAME", binaryMessenger: controller.binaryMessenger)
methodChannel.setMethodCallHandler { [weak self] (call, result) in
if "getOpenFileUrl" == call.method {
result(self?.filePath)
}
}
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
override func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
print("完整文件地址为====\(url.path)")
let completePath = url.path
var path = ""
if let range = completePath.range(of: "Documents/") {
path = String(completePath.suffix(from: range.upperBound))
}
self.filePath = path
print("Documents后面路径为====\(self.filePath)")
return true
}
}
3. 安卓原生(安卓新手,有误勿怪)
3.1配置,在AndroidManifest.xml增加需要打开的文件类型配置,文件配置参考链接:链接
...
...
...
3.2 原生方法获取文件地址
package com.example.device_repair
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import androidx.annotation.NonNull
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
class MainActivity: FlutterActivity() {
// TODO: CHANGE METHOD CHANNEL NAME
private val CHANNEL = "YOUR_CHANNEL_NAME"
var openPath: String? = null
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
val channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
channel.setMethodCallHandler { call, result ->
if (call.method == "getOpenFileUrl") {
result.success(openPath)
openPath = null
} else {
result.notImplemented()
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
handleOpenFileUrl(intent)
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
handleOpenFileUrl(intent)
}
private fun handleOpenFileUrl(intent: Intent?) {
var uri: Uri? = intent?.data
if (uri == null) {
uri = intent?.getParcelableExtra(Intent.EXTRA_STREAM)
}
//获取文件真实地址
val filePath: String? = UriUtils.getFileFromUri(activity, uri)
// val path = intent?.data?.path
if (filePath != null) {
openPath = filePath
}
}
}
3.2.1 andriod获取真实路径 链接
package com.example.device_repair
import android.content.ContentResolver
import android.content.Context
import android.database.Cursor
import android.net.Uri
import android.provider.MediaStore
import java.io.*
object UriUtils {
/**
* 获取真实路径
*
* @param context
*/
fun getFileFromUri(context: Context, uri: Uri?): String? {
return if (uri == null) {
null
} else when (uri.getScheme()) {
ContentResolver.SCHEME_CONTENT -> //Android7.0之后的uri content:// URI
getFilePathFromContentUri(context, uri)
ContentResolver.SCHEME_FILE -> //Android7.0之前的uri file://
File(uri.getPath()).getAbsolutePath()
else -> File(uri.getPath()).getAbsolutePath()
}
}
/**
* 从uri获取path
*
* @param uri content://media/external/file/109009
*
*
* FileProvider适配
* content://com.tencent.mobileqq.fileprovider/external_files/storage/emulated/0/Tencent/QQfile_recv/
* content://com.tencent.mm.external.fileprovider/external/tencent/MicroMsg/Download/
*/
private fun getFilePathFromContentUri(context: Context, uri: Uri?): String? {
if (null == uri) return null
var data: String? = null
val filePathColumn =
arrayOf(MediaStore.MediaColumns.DATA, MediaStore.MediaColumns.DISPLAY_NAME)
val cursor: Cursor? = context.contentResolver.query(uri, filePathColumn, null, null, null)
if (null != cursor) {
if (cursor.moveToFirst()) {
val index: Int = cursor.getColumnIndex(MediaStore.MediaColumns.DATA)
data = if (index > -1) {
cursor.getString(index)
} else {
val nameIndex: Int = cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME)
val fileName: String = cursor.getString(nameIndex)
getPathFromInputStreamUri(context, uri, fileName)
}
}
cursor.close()
}
return data
}
/**
* 用流拷贝文件一份到自己APP私有目录下
*
* @param context
* @param uri
* @param fileName
*/
private fun getPathFromInputStreamUri(context: Context, uri: Uri, fileName: String): String? {
var inputStream: InputStream? = null
var filePath: String? = null
if (uri.authority != null) {
try {
inputStream = context.contentResolver.openInputStream(uri)
val file = createTemporalFileFrom(context, inputStream, fileName)
filePath = file!!.path
} catch (e: Exception) {
} finally {
try {
if (inputStream != null) {
inputStream.close()
}
} catch (e: Exception) {
}
}
}
return filePath
}
@Throws(IOException::class)
private fun createTemporalFileFrom(
context: Context,
inputStream: InputStream?,
fileName: String
): File? {
var targetFile: File? = null
if (inputStream != null) {
var read: Int
val buffer = ByteArray(8 * 1024)
//自己定义拷贝文件路径
targetFile = File(context.externalCacheDir, fileName)
if (targetFile.exists()) {
targetFile.delete()
}
val outputStream: OutputStream = FileOutputStream(targetFile)
while (inputStream.read(buffer).also { read = it } != -1) {
outputStream.write(buffer, 0, read)
}
outputStream.flush()
try {
outputStream.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
return targetFile
}
}
4.如何调用 (可以根据path使用path_provider创建file,然后做上传服务器后台之类的操作)
import 'package:flutter/material.dart';
import 'package:file_share/file_share.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key key}) : super(key: key);
@override
State createState() => _MyAppState();
}
class _MyAppState extends State with WidgetsBindingObserver {
/// 文件路径 (安卓为全路径,iOS为沙盒Documents下面的路径)
var _filePath = "";
@override
void initState() {
super.initState();
getOpenFileUrl();
}
/// 获取分享到的excel文件
getOpenFileUrl() async {
String url = "";
url = await FileShare.getOpenFileUrl();
setState(() {
_filePath = url;
});
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
switch (state) {
case AppLifecycleState.inactive: // 处于这种状态的应用程序应该假设它们可能在任何时候暂停。
print("处于这种状态的应用程序应该假设它们可能在任何时候暂停。");
break;
case AppLifecycleState.resumed: // 应用程序可见,前台
print("应用程序可见,前台");
getOpenFileUrl();
break;
case AppLifecycleState.paused: // 应用程序不可见,后台
print("应用程序不可见,后台");
break;
case AppLifecycleState.detached: // 申请将暂时暂停
print("申请将暂时暂停");
break;
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: Center(
child: Text('文件地址为: $_filePath'),
),
),
);
}
}