1.Flutter部分
構(gòu)建一個(gè)類 用于調(diào)用原生方法 代碼如下
class FileShare {
static const MethodChannel _channel = MethodChannel('YOUR_CHANNEL_NAME'); //channel名字
/// 獲取分享的文件地址
static Future<String> getOpenFileUrl() async {
var path = "";
path = await _channel.invokeMethod('getOpenFileUrl'); //方法名
return path;
}
}
2.iOS原生
2.1配置支鸡,在info.plist增加需要打開的文件類型配置,iOS文件配置參考鏈接:鏈接
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
...
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeName</key>
<string>Microsoft Excel</string>
<key>LSHandlerRank</key>
<string>Alternate</string>
<key>LSItemContentTypes</key>
<array>
<string>com.microsoft.excel.xls</string>
<string>org.openxmlformats.spreadsheetml.sheet</string>
</array>
</dict>
</array>
</dict>
</plist>
2.2 原生方法獲取文件地址(此處注意Flutter框架會(huì)報(bào)錯(cuò)状婶,不過運(yùn)行是沒有問題的)
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
///調(diào)用的方法 (channel名字與flutter統(tǒng)一)
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增加需要打開的文件類型配置,文件配置參考鏈接:鏈接
...
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:exported="true"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
...
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="application/vnd.ms-excel" />
<data android:mimeType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" />
</intent-filter>
</activity>
...
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)
}
//獲取文件真實(shí)地址
val filePath: String? = UriUtils.getFileFromUri(activity, uri)
// val path = intent?.data?.path
if (filePath != null) {
openPath = filePath
}
}
}
3.2.1 andriod獲取真實(shí)路徑 鏈接
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 {
/**
* 獲取真實(shí)路徑
*
* @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<String>(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.如何調(diào)用 (可以根據(jù)path使用path_provider創(chuàng)建file拦盹,然后做上傳服務(wù)器后臺(tái)之類的操作)
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<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> 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: // 處于這種狀態(tài)的應(yīng)用程序應(yīng)該假設(shè)它們可能在任何時(shí)候暫停。
print("處于這種狀態(tài)的應(yīng)用程序應(yīng)該假設(shè)它們可能在任何時(shí)候暫停普舆。");
break;
case AppLifecycleState.resumed: // 應(yīng)用程序可見恬口,前臺(tái)
print("應(yīng)用程序可見,前臺(tái)");
getOpenFileUrl();
break;
case AppLifecycleState.paused: // 應(yīng)用程序不可見沼侣,后臺(tái)
print("應(yīng)用程序不可見祖能,后臺(tái)");
break;
case AppLifecycleState.detached: // 申請(qǐng)將暫時(shí)暫停
print("申請(qǐng)將暫時(shí)暫停");
break;
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: Center(
child: Text('文件地址為: $_filePath'),
),
),
);
}
}