小知识,大挑战!本文正在参与“ 程序员必备小知识 ”创作活动
以下分别介绍react-native选择文件功能,原生的支持,通过桥接文件,进行RN与原生的交互,来实现获取文件操作
iOS端选择文件功能
iOS端原生实现选择文件使用的是UIDocumentPickerViewController这个库。 具体代码如下:
class FilePickerUtil: NSObject,UIDocumentPickerDelegate {
func show(){
let documentTypes = ["public.content",
"public.text",
"public.source-code",
"public.image",
"public.audiovisual-content",
"com.adobe.pdf",
"com.apple.keynote.key",
"com.microsoft.word.doc",
"com.microsoft.excel.xls",
"com.microsoft.powerpoint.ppt"]
let document = UIDocumentPickerViewController.init(documentTypes: documentTypes, in: .open)
document.delegate = self
document.allowsMultipleSelection = **true**
UIApplication.shared.keyWindow?.rootViewController?.present(document, animated: true, completion: **nil**)
}
}
其中UIDocumentPickerDelegate代理方法。
func documentPickerWasCancelled( _controller: UIDocumentPickerViewController) {
controller.dismiss(animated: true, completion: nil)
}
func documentPicker( _controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
for url in urls {
do {
let resources = **try** url.resourceValues(forKeys:[.fileSizeKey])
let fileSize = resources.fileSize!
print("文件大小:" + String(fileSize))
print(fileSize != 0);
let mediaItem = [
"path": url.absoluteString,
"type": "file",
"name": url.lastPathComponent,
"fileSize": fileSize != 0 ? Double(fileSize) / 1024.0 / 1024.0 : 0
] as [String : Any]
print("文件路径:" + url.absoluteString)
} catch {
print( "文件获取失败")
}
}
controller.dismiss(animated: true, completion: nil)
}
以上在模拟器上没有问题,真机上出现问题,1,没有权限,2,无法预览。解决如下: 如果想copy文件到手机其他位置:
let isSecuredURL = url.startAccessingSecurityScopedResource()
let resources = try url.resourceValues(forKeys:[.fileSizeKey])
let filePath = documentPath(subFolder:"file").appendingPathComponent(url.lastPathComponent).path;
let fileData = NSData.init(contentsOf: url);
fileData?.write(toFile: filePath, atomically: true)
if (isSecuredURL) {
url.stopAccessingSecurityScopedResource()
}
/// 返回文档路径
func documentPath(subFolder: String?) -> URL {
let url = URL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(
FileManager.SearchPathDirectory.documentDirectory,
FileManager.SearchPathDomainMask.userDomainMask, true
).first!)
if (subFolder == nil) {
return url
}
let result = url.appendingPathComponent(subFolder!)
// 没有文件夹,尝试创建文件夹
if (!FileManager.default.fileExists(atPath: result.path)) {
do {
try FileManager.default.createDirectory(at: result, withIntermediateDirectories: true, attributes: nil)
} catch {
return url
}
}
return result
}
android端选择文件功能
使用Intent进行选择文件功能,具体使用代码如下:MEDIA_REQUEST_FILEPICK为自己定义的值,用于在结果中判断回调
private var tag = "FilePickerUtil"
private val DOC = "application/msword"
private val XLS = "application/vnd.ms-excel"
private val PPT = "application/vnd.ms-powerpoint"
private val XLS1 = "application/x-excel"
private val DOCX = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
private val XLSX = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
private val PPTX = "application/vnd.openxmlformats-officedocument.presentationml.presentation"
private val PDF = "application/pdf"
val activity = context.currentActivity
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE,true)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
val mimeTypes = arrayOf(DOC, DOCX, PDF, PPT, PPTX, XLS, XLS1, XLSX)
intent.type = "application/*"
intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes)
activity?.startActivityForResult(intent, MEDIA_REQUEST_FILEPICK)
得到的结果在onActivityResult方法里面获取
public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
// 选文件
if (requestCode == Constant.MEDIA_REQUEST_FILEPICK) {
//文件信息
result = this.getFileResult(context, data);
}
}
统一调用方法:
fun getFileResult(context: ReactContext, data: Intent?): WritableArray {
val result = Arguments.createArray()
if (data == null) {
return result
}
if (data.clipData != null) {
//多个文件
val count: Int = data.clipData!!.itemCount
var currentIndex = 0;
while (currentIndex < count) {
result.pushMap(getFileInfo(context, data.clipData!!.getItemAt(currentIndex).uri));
currentIndex += 1
}
} else if (data.data != null) {
result.pushMap(getFileInfo(context, data.data!!));
}
return result
}
getFileInfo 是获取对象的地址,这块做的比较复杂的地方是将选择到的文件地址copy到指定文件中去,方便以后进行上传操作 方法如下:其中FileUtil为公共类。
private fun getFileInfo(context: ReactContext, uri: Uri): WritableMap? {
val map = Arguments.createMap()
val fileInfo: Map<String, String> = FileUtil.getFileInfoByUri(context, uri) as Map<String, String>
var fileSize: Double? = fileInfo["size"]?.toDouble()
var fileName: String? = fileInfo["filename"];
var filePath = "";
if (fileSize != null && fileSize > 25 * 1024 * 1024) {
// 文件太大不允许copy
map.putDouble("fileSize", fileSize)
map.putString("type", "file")
map.putString("name", fileName);
return map;
}
if (fileName!= null) {
var extension: String = fileName!!.split('.')[1];
filePath = FileUtil.saveOriginalFile(context, uri, extension)
}
map.putString("path", filePath)
map.putString("type", "file")
map.putString("name", fileName);
if (fileSize != null) {
map.putDouble("fileSize", fileSize)
};
return map
}
其中FileUtil涉及到的方法如下:
/**
* 获取文件信息 包括文件名,文件大小
* @param uri uri
* @return
*/
public static Map getFileInfoByUri(Context context, Uri uri) {
String filename = "";
String size = "";
Map<String,String> mMap = new HashMap<String,String>();
Cursor returnCursor = context.getContentResolver().query(uri, null,
null, null, null);
if (returnCursor != null) {
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
returnCursor.moveToFirst();
filename = returnCursor.getString(nameIndex);
mMap.put("filename", filename);
int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE);
returnCursor.moveToFirst();
size = returnCursor.getString(sizeIndex);
mMap.put("size", size.toString());
returnCursor.close();
}
return mMap;
}
/**
* copy文件到指定存储位置
* @param uri uri
* @return
*/
public static String saveOriginalFile(Context ctx, Uri uri, String extension) {
try {
String path = FileUtil.externalPath(ctx, "Files").getAbsolutePath() + '/' + UUID.randomUUID().toString() + '.' + extension;
FileUtil.copy(ctx, uri, path);
return path;
} catch (Exception exception) {
Logger.e(TAG, exception);
return "";
}
}
/**
* 返回文档路径,子文件夹不存在会创建
*
* @param context context
* @param subFolder subFolder
* @return file
*/
public static File externalPath(Context context, String subFolder) {
// 不指定 type,使用 files 根目录
File folder = context.getExternalFilesDir("");
if (subFolder == null) {
return folder;
}
File full = new File(folder, subFolder);
if (!full.exists()) {
boolean success = full.mkdirs();
if (!success) {
return folder;
}
}
return full;
}
/**
* 根据Uri拷贝文件
*
* @param context Context
* @param uri Uri
* @param dst String destination
* @throws IOException exception
*/
public static void copy(Context context, Uri uri, String dst) throws IOException {
try (AssetFileDescriptor fileDescriptor = getDescriptorFromUri(context, uri)) {
if (fileDescriptor == null) {
throw new IOException("Copy from uri failed: open descriptor failed");
}
FileInputStream inStream = fileDescriptor.createInputStream();
FileOutputStream outStream = new FileOutputStream(dst);
FileChannel inChannel = inStream.getChannel();
FileChannel outChannel = outStream.getChannel();
inChannel.transferTo(0, inChannel.size(), outChannel);
inStream.close();
outStream.close();
} catch (IOException e) {
// 如果出现了IO异常或读取文件描述符异常,则尝试使用从路径中读写文件
} catch (Exception e) {
throw e;
}
}
以上就是选择文件的全部内容,具体怎么上传到服务器,这个就不多说啦,因为路径已经可以拿到啦,剩下的很简单了。