大家好今天繼續示範文件上傳至雲端的功能。
我們先新增一個名為FileUtil的Java Class:
import android.provider.MediaStore;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import java.io.File;
import java.util.Locale;
public class FileUtil {
/**
* 根据Uri獲取檔案路徑
* @param context context
* @param uri uri
*/
public static String getFileAbsolutePath(Context context, Uri uri) {
if (context == null || uri == null) return null;
// DocumentProvider
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && DocumentsContract.isDocumentUri(context, uri)) {
if (isExternalStorageDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
if ("primary".equalsIgnoreCase(type)) {
return Environment.getExternalStorageDirectory() + "/" + split[1];
}else if ("home".equalsIgnoreCase(type)) {
return Environment.getExternalStorageDirectory() + "/documents/" + split[1];
}
} else if (isDownloadsDocument(uri)) {
// DownloadsProvider
final String id = DocumentsContract.getDocumentId(uri);
if (TextUtils.isEmpty(id)) {
return null;
}
if (id.startsWith("raw:")) {
return id.substring(4);
}
String[] contentUriPrefixesToTry = new String[]{
"content://downloads/public_downloads",
"content://downloads/my_downloads",
"content://downloads/all_downloads"
};
for (String contentUriPrefix : contentUriPrefixesToTry) {
try {
Uri contentUri = ContentUris.withAppendedId(Uri.parse(contentUriPrefix), Long.valueOf(id));
String path = getDataColumn(context, contentUri, null, null);
if (path != null) {
return path;
}
} catch (Exception ignore) {
}
}
try {
String path = getDataColumn(context, uri, null, null);
if (path != null) {
return path;
}
} catch (Exception ignore) {
}
// path could not be retrieved using ContentResolver, therefore copy file to accessible cache using streams
return null;
} else if (isMediaDocument(uri)) {
// MediaProvider
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
Uri contentUri;
switch (type.toLowerCase(Locale.ENGLISH)) {
case "image":
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
break;
case "video":
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
break;
case "audio":
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
break;
default:
contentUri = MediaStore.Files.getContentUri("external");
break;
}
final String selection = MediaStore.MediaColumns._ID +"=?";
final String[] selectionArgs = new String[]{split[1]};
return getDataColumn(context, contentUri, selection, selectionArgs);
}
} else if (ContentResolver.SCHEME_CONTENT.equalsIgnoreCase(uri.getScheme())) {
// MediaStore (and general)
// Return the remote address
if (isGooglePhotosUri(uri)) {
return uri.getLastPathSegment();
}
return getDataColumn(context, uri, null, null);
} else if (ContentResolver.SCHEME_FILE.equalsIgnoreCase(uri.getScheme())) {
// File
return uri.getPath();
}
return null;
}
public static String getDataColumn(Context context, @NonNull Uri uri, String selection, String[] selectionArgs) {
Cursor cursor = null;
String column = MediaStore.Images.Media.DATA;
String[] projection = {column};
try {
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
if (cursor != null && cursor.moveToFirst()) {
int index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(index);
}
} catch (Exception ignore) {
} finally {
if (cursor != null)
cursor.close();
}
return null;
}
//檔案名
public static String fileName(String path){
if( path == null || path.isEmpty() ) return "";
File file = new File(path);
if( !file.exists() || !file.isFile() ) {
return "";
}
String filename = file.getName();
// String ext = FileKit.getFileExt(filename);
// if( !ext.equalsIgnoreCase(".csv") ){
// return "";
// }
return filename;
}
public static boolean isExternalStorageDocument(Uri uri) {
return "com.android.externalstorage.documents".equals(uri.getAuthority());
}
public static boolean isDownloadsDocument(Uri uri) {
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}
public static boolean isMediaDocument(Uri uri) {
return "com.android.providers.media.documents".equals(uri.getAuthority());
}
public static boolean isGooglePhotosUri(Uri uri) {
return "com.google.android.apps.photos.content".equals(uri.getAuthority());
}
}
這一個class的功用是將選擇檔案的路徑由Uri轉為String和拿到檔案的名字,各位也可以去網路上搜尋其他的方法。
接下來我們再新增另一個Java Class
GoogleDriveServiceFunction:
public class GoogleDriveServiceFunction {
private final Executor mExecutor = Executors.newSingleThreadExecutor();
Drive mDriveService;
public GoogleDriveServiceFunction(Drive mDriveService) {
this.mDriveService = mDriveService;
}
public Task<String> createFile(String filePath, String fileName){
return Tasks.call(mExecutor,() ->{
Log.e("file", filePath+","+fileName );
File fileMetaData = new File();
fileMetaData.setName(fileName);
java.io.File file = new java.io.File(filePath);
FileContent mediaContent = new FileContent("application/octet-stream",file);
File myFile = null;
try{
myFile = mDriveService.files().create(fileMetaData,mediaContent)
.execute();
Log.e("File" ,"Flie ID = " +myFile.getId());
}catch (Exception e){
e.printStackTrace();
}
if (myFile == null){
throw new IOException("Null result when request file creation");
}
return myFile.getId();
});
}
public Task<String> createFolder(){
return Tasks.call(mExecutor,() ->{
File fileMetaData = new File();
fileMetaData.setName("Test1");
fileMetaData.setMimeType("application/vnd.google-apps.folder");
File folder = mDriveService.files().create(fileMetaData)
.setFields("id")
.execute();
Log.e("folderid", "createFolder: " + folder.getId() );
return folder.getId();
});
}
public Task<String> searchFile(){
return Tasks.call(mExecutor,() ->{
String pageToken = null;
try{
do {
FileList reset =mDriveService.files().list()
.setQ("'xxxxxxxxxx@gmail.com' in writers")
.setSpaces("drive")
.setFields("nextPageToken, files(id,name,mimeType)")
.setPageToken(pageToken)
.execute();
for (File file : reset.getFiles()){
Log.e("File", "File: "+file.getName()+","+file.getId()+","+file.getMimeType());
}
pageToken = reset.getNextPageToken();
}while (pageToken !=null);
}catch (UserRecoverableAuthIOException e) {
e.printStackTrace();
Log.e("err",e.getMessage());
} catch (IOException e) {
e.printStackTrace();
Log.e("err",e.getMessage());
}
return null;
});
}
}
createFile程式碼的FileContent mediaContent = new FileContent("application/octet-stream",file);是指上傳檔案的格式是二進制檔案。
createFolder程式碼的fileMetaData.setMimeType("application/vnd.google-apps.folder");這一行是在雲端硬碟的資料夾的mimeType格式。
searchFile程式碼得setQ可以設定搜尋的條件,我們可以由下面的網址去設定我們要使用的條件。
在MainActivity加上以下的程式碼:
private static final int PICK_IMAGE_FROM_GALLERY_REQUEST_CODE = 300;
private Button btnChooseFile;
private Button btnSearchGD;
private Button btnCreateFolder;
在MainActivity的onCreate加上:
protected void onCreate(Bundle savedInstanceState) {
...
init();
}
private void init(){
btnChooseFile = findViewById(R.id.btnChooseFile);
btnCreateFolder = findViewById(R.id.btnCreateFolder);
btnSearchGD = findViewById(R.id.btnSearchGoogledrive);
btnChooseFile.setOnClickListener(this);
btnCreateFolder.setOnClickListener(this);
btnSearchGD.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btnChooseFile:
chooseFile();
break;
case R.id.btnCreateFolder:
createFolder();
break;
case R.id.btnSearchGoogledrive:
searchGoogleDrive();
break;
}
}
private void searchGoogleDrive() {
googleDriveServiceFunction.searchFile();
}
private void createFolder() {
googleDriveServiceFunction.createFolder();
}
public void chooseFile(){
//Create an Intent with action as ACTION_GET_CONTENT
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
//設定選擇的檔案格式
intent.setType("image/*");
//檔案複選True
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
//We pass an extra array with the accepted mime types. This will ensure only components with these MIME types as targeted.
String[] mimeTypes = {"image/jpeg", "image/png"};
intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes);
// Launching the Intent
startActivityForResult(intent, PICK_IMAGE_FROM_GALLERY_REQUEST_CODE);
}
上面intent.setType的功能是設定選擇的檔案格式,可以是圖片、影片、或是csv文件等等,我們可以藉由https://www.jianshu.com/p/c1656748849f
這個網址去尋找文件的mimeType。
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)是設定檔案是否可以複選,我們這邊先示範用圖片去上傳檔案。
在onActivityResult裡面加上:
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
...
if (requestCode == PICK_IMAGE_FROM_GALLERY_REQUEST_CODE && resultCode == RESULT_OK) {
ArrayList<String> uriList = new ArrayList<String>();
if (data.getClipData() != null) {
for (int i = 0; i < data.getClipData().getItemCount(); i++) {
uriList.add(FileUtil.getFileAbsolutePath(this,data.getClipData().getItemAt(i).getUri()));
Log.e("FilePath", "" + uriList.get(i));
}
} else if (Build.VERSION.SDK_INT >= 16 && data.getClipData() == null) {
uriList.add(FileUtil.getFileAbsolutePath(this,data.getData()));
Log.e("FilePath", "" + uriList.get(0));
}
//照片的uri
uploadFile(uriList);
}
}
public void uploadFile(ArrayList<String> uriList){
ArrayList<Uri> uris = null;
for (int i=0;i<uriList.size();i++){
Log.e("eee",""+uriList.get(i));
googleDriveServiceFunction.createFile(uriList.get(i),FileUtil.fileName(uriList.get(i)))
.addOnSuccessListener(new OnSuccessListener<String>() {
@Override
public void onSuccess(String s) {
Toast.makeText(getApplicationContext(), "Uploaded successfully", Toast.LENGTH_LONG).show();
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
Toast.makeText(getApplicationContext(), "Check your google api key", Toast.LENGTH_LONG).show();
Log.e("error",e.getMessage());
}
});
}
}
如果版本是android Q 的話,我們要在AndroidManifest.xml的application裡面加上
android:requestLegacyExternalStorage="true"
否則會上傳失敗。
這樣一來,上傳檔案至雲端硬碟就告一段落了。