COS騰訊雲對象存儲的集成

本篇博客談的是前段時間接觸的騰訊雲對象存儲的集成和具體使用。sdk並不複雜,主要是騰訊雲的文檔沒怎麼更新,很多地方講解的也不清晰,我已多次入坑所以想要寫一篇相關的博客,好了進入正題。

基礎版Demo:http://download.csdn.net/detail/g_ying_jie/9909448

功能演進版Demo:http://download.csdn.net/detail/g_ying_jie/9924321

仿微雲終極Demo:http://download.csdn.net/download/g_ying_jie/9959175


第一步、註冊騰訊雲:https://www.qcloud.com/register


第二步、登陸控制檯創建存儲桶:https://console.qcloud.com/cos4/bucket


第三步、下載Android SDK並集成so庫和jar包,配置相關環境和權限(以前的騰訊雲直播~上談過很多了這裏就不再提了),相關官網鏈接


第四步、初始化COSClient對象,對應demo的BizService類有詳細用法

String appid =  "騰訊雲註冊的appid";
Context context = getApplicationContext();
String peristenceId = "持久化Id";

//創建COSClientConfig對象,根據需要修改默認的配置參數
COSClientConfig config = new COSClientConfig();
/**
 * 設置園區;根據創建的cos空間時選擇的園區
 * 華南園區:gz 或 COSEndPoint.COS_GZ(已上線)
 * 華北園區:tj 或 COSEndPoint.COS_TJ(已上線)
 * 華東園區:sh 或 COSEndPoint.COS_SH
 */
config.setEndPoint(COSEndPoint.COS_GZ);

//創建COSlient對象,實現對象存儲的操作
COSClient cos = new COSClient(context,appid,config,peristenceId);

第五步、生成簽名,詳細用法可以下載demo的BizService類

   這一步很重要,也是攔住了很多人的第一個坎,因爲騰訊官方用的PHP作爲示例,更令人無語的是官方提供的Android SDK的demo沒有提到拼接簽名,demo直接請求騰訊的服務器獲取的單次有效和多次有效簽名。廢話不多說,下面介紹Android端如何獲取單次有效和多次有效簽名




①生成單次有效簽名

public String getSignOnce(String cosPath) {
        try {
            String Original = getSignOriginalOnce(cosPath);
            byte[] HmacSHA1 = HmacSHA1Encrypt(SecretKey, Original);
            byte[] all = new byte[HmacSHA1.length + Original.getBytes(ENCODING).length];
            System.arraycopy(HmacSHA1, 0, all, 0, HmacSHA1.length);
            System.arraycopy(Original.getBytes(ENCODING), 0, all, HmacSHA1.length, Original.getBytes(ENCODING).length);
            String SignData = Base64Util.encode(all);
            return SignData;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "get sign failed";
}

/**
     * @param SecretKey   密鑰
     * @param EncryptText 簽名串
     */
    private byte[] HmacSHA1Encrypt(String SecretKey, String EncryptText) throws Exception {
        byte[] data = SecretKey.getBytes(ENCODING);
        javax.crypto.SecretKey secretKey = new SecretKeySpec(data, MAC_NAME);
        Mac mac = Mac.getInstance(MAC_NAME);
        mac.init(secretKey);
        byte[] text = EncryptText.getBytes(ENCODING);
        return mac.doFinal(text);
}
/**
 *getLinuxDateSimple()獲取時間戳,單位秒
 * getRandomTenStr()獲取隨機數
 */
private String getSignOriginalOnce(String cosPath) {
        return String.format(
                "a=%s&b=%s&k=%s&e=%s&t=%s&r=%s&f=%s",
                appid,
                bucket,
                SecretId,
                "0",
                String.valueOf(getLinuxDateSimple()),
                getRandomTenStr(),
                "/" + appid + "/" + bucket + "/" + cosPath);
}

②生成多次有效簽名

public String getSign() {
        try {
            String Original = getSignOriginal();
            byte[] HmacSHA1 = HmacSHA1Encrypt(SecretKey, Original);
            byte[] all = new byte[HmacSHA1.length + Original.getBytes(ENCODING).length];
            System.arraycopy(HmacSHA1, 0, all, 0, HmacSHA1.length);
            System.arraycopy(Original.getBytes(ENCODING), 0, all, HmacSHA1.length, Original.getBytes(ENCODING).length);
            String SignData = Base64Util.encode(all);
            return SignData;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "get sign failed";
}
//e=%s表示簽名到期時間戳
private String getSignOriginal() {
        return String.format(
                "a=%s&b=%s&k=%s&e=%s&t=%s&r=%s&f=",
                appid,
                bucket,
                SecretId,
                String.valueOf(getLinuxDateSimple() + 60),
                String.valueOf(getLinuxDateSimple()),
                getRandomTenStr());
}

第六步,目錄操作

①創建目錄

此處注意:createDirRequest.setBiz_attr(biz_attr);這個請求參數請不要添加,會報-5999參數出錯的錯誤碼。具體原因不明但肯定不是參數錯誤的原因,因爲其他的目錄包括文件操作都是正常的只有目錄創建會失敗,我用官方的demo同樣存在這個問題,已經反饋給騰訊客服。

/**
  * 創建多層目錄 dirName = gu/test;  dirName.length()<=20
*/
    public void createDir() {
        final String dirName = "gu/test";
        new Thread(new Runnable() {
            @Override
            public void run() {
                CreateDir.crateDir(bizService, dirName);
            }
        }).start();
}
public static void crateDir(BizService bizService, String cosPath) {
        /** CreateDirRequest 請求對象 */
        CreateDirRequest createDirRequest = new CreateDirRequest();
        /** 設置Bucket */
        createDirRequest.setBucket(bizService.bucket);
        /** 設置cosPath :遠程路徑*/
        createDirRequest.setCosPath(cosPath);
        /** 設置sign: 簽名,此處使用多次簽名 */
        createDirRequest.setSign(bizService.getSign());
        /** 設置listener: 結果回調 */
        createDirRequest.setListener(new ICmdTaskListener() {
            @Override
            public void onSuccess(COSRequest cosRequest, COSResult cosResult) {
                CreateDirResult createDirResult = (CreateDirResult) cosResult;
                String result = "目錄創建: ret=" + createDirResult.code + "; msg=" + createDirResult.msg
                        + "ctime = " + createDirResult.ctime;
                Log.w("XIAO", result);
            }

            @Override
            public void onFailed(COSRequest cosRequest, COSResult cosResult) {
                String result = "目錄創建失敗:ret=" + cosResult.code + "; msg =" + cosResult.msg;
                Log.w("XIAO", result);
            }
        });
        /** 發送請求:執行 */
        bizService.cosClient.createDir(createDirRequest);
}
②目錄列表查詢

/**
     * 1)bucket根目錄自動展示   cosPath = "/"
     * 2)指定目錄下的目錄列表展示  cosPath = "test/"
     * 3)目錄前綴查詢,存在的關鍵字      cosPath = "test/" ;  listDirRequest.setPrefix("t");
     * 4)設置顯示數值,1, 10, 100  最大1000
     */
    public void listDir() {
        final String dirName = "/";
        //前綴查詢的字符串,爲空表示不進行精確查詢
        final String prefix = "";
        new Thread(new Runnable() {
            @Override
            public void run() {
                ListDir.listDir(bizService, dirName, prefix);
            }
        }).start();
}
public static void listDir(BizService bizService, String cosPath, String prefix) {
        /** ListDirRequest 請求對象 */
        ListDirRequest listDirRequest = new ListDirRequest();
        /** 設置Bucket */
        listDirRequest.setBucket(bizService.bucket);
        /** 設置cosPath :遠程路徑*/
        listDirRequest.setCosPath(cosPath);
        /** 設置num :預查詢的目錄數*/
        listDirRequest.setNum(100);
        /** 設置content: 透傳字段,首次拉取必須清空。拉取下一頁,需要將前一頁返回值中的context透傳到參數中*/
        listDirRequest.setContent("");
        /** 設置sign: 簽名,此處使用多次簽名 */
        listDirRequest.setSign(bizService.getSign());
        /** 設置listener: 結果回調 */
        listDirRequest.setListener(new ICmdTaskListener() {
            @Override
            public void onSuccess(COSRequest cosRequest, COSResult cosResult) {
                ListDirResult listObjectResult = (ListDirResult) cosResult;
                //文件夾 =》不含有  filesize 或 sha 或 access_url 或 souce_url
                StringBuilder stringBuilder = new StringBuilder("目錄列表查詢結果如下:");
                stringBuilder.append("code =" + listObjectResult.code + "; msg =" + listObjectResult.msg + "\n");
                stringBuilder.append("list是否結束:").append(listObjectResult.listover).append("\n");
                stringBuilder.append("content = " + listObjectResult.context + "\n");
                if (listObjectResult.infos != null && listObjectResult.infos.size() > 0) {
                    int length = listObjectResult.infos.size();
                    String str;
                    JSONObject jsonObject;
                    StringBuilder fileStringBuilder = new StringBuilder();
                    StringBuilder dirStringBuilder = new StringBuilder();
                    for (int i = 0; i < length; i++) {
                        str = listObjectResult.infos.get(i);
                        try {
                            jsonObject = new JSONObject(str);
                            if (jsonObject.has("sha")) {
                                //是文件
                                fileStringBuilder.append("文件:" + jsonObject.optString("name")).append("\n");
                            } else {
                                //是文件夾
                                dirStringBuilder.append("文件夾: " + jsonObject.optString("name")).append("\n");
                            }
                        } catch (JSONException e) {
                            e.printStackTrace();
                        }
                    }
                    stringBuilder.append(fileStringBuilder);
                    stringBuilder.append(dirStringBuilder);
                } else {
                    stringBuilder.append("該目錄下無內容");
                }
                String result = stringBuilder.toString();
                Log.w("XIAO", result);
            }

            @Override
            public void onFailed(COSRequest cosRequest, COSResult cosResult) {
                String result = "目錄查詢失敗:ret=" + cosResult.code + "; msg =" + cosResult.msg;
                Log.w("XIAO", result);
            }
        });
        /** 設置 prefix: 前綴查詢的字符串,開啓前綴查詢 */
        if (!TextUtils.isEmpty(prefix) && prefix != null) {
            listDirRequest.setPrefix(prefix);
        }
        /** 發送請求:執行 */
        bizService.cosClient.listDir(listDirRequest);
}
③目錄刪除

public void deleteDir() {
        final String dirName = "empty/";
        new Thread(new Runnable() {
            @Override
            public void run() {
                RemoveEmptyDir.removeEmptyDir(bizService, dirName);
            }
        }).start();
}
public static void removeEmptyDir(BizService bizService, String cosPath) {
        /** RemoveEmptyDirRequest 請求對象,只能刪除空文件夾,其他無效 */
        RemoveEmptyDirRequest removeEmptyDirRequest = new RemoveEmptyDirRequest();
        /** 設置Bucket */
        removeEmptyDirRequest.setBucket(bizService.bucket);
        /** 設置cosPath :遠程路徑*/
        removeEmptyDirRequest.setCosPath(cosPath);
        /** 設置sign: 簽名,此處使用單次簽名 */
        removeEmptyDirRequest.setSign(bizService.getSignOnce(cosPath));
        /** 設置listener: 結果回調 */
        removeEmptyDirRequest.setListener(new ICmdTaskListener() {
            @Override
            public void onSuccess(COSRequest cosRequest, COSResult cosResult) {
                String result = "code =" + cosResult.code + "; msg =" + cosResult.msg;
                Log.w("XIAO", result);
            }

            @Override
            public void onFailed(COSRequest cosRequest, COSResult cosResult) {
                String result = "code =" + cosResult.code + "; msg =" + cosResult.msg;
                Log.w("XIAO", result);
            }
        });
        /** 發送請求:執行 */
        bizService.cosClient.removeEmptyDir(removeEmptyDirRequest);
}

深入探究:COS並沒有提供非空文件夾的刪除接口,那麼怎麼實現該功能呢

/**
     * 遞歸刪除文件和文件夾
     */
    private List<FileItem> childFile = new ArrayList<FileItem>();

    private void RecursionDeleteFile(final FileItem item) {
        if (item.getType() == 0) {
            onDelete(item.getCosPath(), 0);
            return;
        }
        if (item.getType() == 1) {
            //前綴查詢的字符串,爲空表示不進行精確查詢
            final String prefix = "";
            ListDirRequest dirRequest = DirUtil.getListDirRequest(bizService, item.getCosPath(), prefix);
            dirRequest.setListener(new ICmdTaskListener() {
                @Override
                public void onSuccess(COSRequest cosRequest, COSResult cosResult) {
                    if (!childFile.isEmpty())
                        childFile.clear();
                    DirUtil.getData(cosResult, childFile, cosRequest.getCosPath());
                    if (childFile == null || childFile.size() == 0) {
                        onDelete(item.getCosPath(), 1);
                        return;
                    }
                    for (FileItem f : childFile) {
                        RecursionDeleteFile(f);
                    }
                    onDelete(item.getCosPath(), 1);
                }

                @Override
                public void onFailed(COSRequest cosRequest, COSResult cosResult) {
                    Log.e("DELETE", cosResult.code + "==" + cosResult.msg);
                }
            });
            bizService.cosClient.listDirAsyn(dirRequest);
        }
}
private void onDelete(String path, int type) {
        if (type == 0) {
            DeleteObjectRequest request = ObjectUtil.getDeleteObjRequest(bizService, path);
            request.setListener(this);
            bizService.cosClient.deleteObjectAsyn(request);
        }
        if (type == 1) {
            if (path.equals("/doc/") || path.equals("/music/") || path.equals("/picture/") || path.equals("/video/"))
                return;
            RemoveEmptyDirRequest request = DirUtil.getRemoveDirRequest(bizService, path);
            request.setListener(this);
            bizService.cosClient.removeEmptyDirAsyn(request);
        }
}
其中可以用Javabean類的String屬性存儲列表查詢所得的cosPath以及標記類型(是文件夾或者文件),然後遞歸刪除,具體刪除某項的時候通過類型調用對應的刪除方法


第七步、文件操作

①文件上傳,這裏是第二個坑,騰訊的demo只能夠選取文件不能選取音樂,圖片等資源抓狂

首先跳轉自帶的文件管理器,選定要上傳的內容

protected void onAdd() {
        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
        //intent.setType(“image/*”);
        //intent.setType(“audio/*”); //選擇音頻
        //intent.setType(“video/*”); //選擇視頻 (mp4 3gp 是android支持的視頻格式)
        //intent.setType(“video/*;image/*”);//同時選擇視頻和圖片
        intent.setType("*/*");//無類型限制
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        try {
            startActivityForResult(intent, OPENFILE_CODE);
        } catch (android.content.ActivityNotFoundException ex) {
            Toast.makeText(this, "親,木有文件管理器啊-_-!!", Toast.LENGTH_SHORT).show();
        }
}
然後在onActivityResult獲取返回的URI並解析成文件的絕對路徑

注意:Android 4.4之後獲取的URI與之前的不一樣,單純的uri.getPath()並不能正確獲取選中文件的絕對路徑,需要按照如下方法處理。

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (resultCode != Activity.RESULT_OK || data == null) {
            return;
        }
        switch (requestCode) {
            case OPENFILE_CODE:
                Uri uri = data.getData();
                currentPath = getPath(this, uri);
                localText.setText(currentPath);
                break;
            default:
                break;
        }
}
public static String getPath(final Context context, final Uri uri) {

        final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;

        // DocumentProvider
        if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
            // ExternalStorageProvider
            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];
                }
            }
            // DownloadsProvider
            else if (isDownloadsDocument(uri)) {

                final String id = DocumentsContract.getDocumentId(uri);
                final Uri contentUri = ContentUris.withAppendedId(
                        Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));

                return getDataColumn(context, contentUri, null, null);
            }
            // MediaProvider
            else if (isMediaDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];

                Uri contentUri = null;
                if ("image".equals(type)) {
                    contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
                } else if ("video".equals(type)) {
                    contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
                } else if ("audio".equals(type)) {
                    contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
                }

                final String selection = "_id=?";
                final String[] selectionArgs = new String[]{split[1]};

                return getDataColumn(context, contentUri, selection, selectionArgs);
            }
        }
        // MediaStore (and general)
        else if ("content".equalsIgnoreCase(uri.getScheme())) {

            // Return the remote address
            if (isGooglePhotosUri(uri))
                return uri.getLastPathSegment();

            return getDataColumn(context, uri, null, null);
        }
        // File
        else if ("file".equalsIgnoreCase(uri.getScheme())) {
            return uri.getPath();
        }

        return null;
    }

    public static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {

        Cursor cursor = null;
        final String column = "_data";
        final String[] projection = {column};

        try {
            cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
                    null);
            if (cursor != null && cursor.moveToFirst()) {
                final int index = cursor.getColumnIndexOrThrow(column);
                return cursor.getString(index);
            }
        } finally {
            if (cursor != null)
                cursor.close();
        }
        return null;
    }

    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());
    }
最後上傳文件,騰訊雲對象存儲支持斷點續傳,默認的使用大文件分片上傳的方式會開啓斷點續傳。另小文件簡單上傳可以下載demo,對應的PutObject類有介紹

public void upload2() {
        if (TextUtils.isEmpty(currentPath)) {
            Toast.makeText(FileUploadActivity.this, "請選擇文件", Toast.LENGTH_SHORT).show();
            return;
        }
        new Thread(new Runnable() {
            @Override
            public void run() {
                String filename = FileUtils.getFileName(currentPath);
                String cosPath = "/" + filename; //cos 上的路徑
                PutObject.putObjectForLargeFile(bizService, cosPath, currentPath);
            }
        }).start();
}
/**
     * 大文件分片上傳 : >=20M的文件,需要使用分片上傳,否則會出錯
     */
    public static void putObjectForLargeFile(BizService bizService, String cosPath, String localPath) {
        /** PutObjectRequest 請求對象 */
        PutObjectRequest putObjectRequest = new PutObjectRequest();
        /** 設置Bucket */
        putObjectRequest.setBucket(bizService.bucket);
        /** 設置cosPath :遠程路徑*/
        putObjectRequest.setCosPath(cosPath);
        /** 設置srcPath: 本地文件的路徑 */
        putObjectRequest.setSrcPath(localPath);
        /** 設置 insertOnly: 是否上傳覆蓋同名文件*/
        putObjectRequest.setInsertOnly("1");
        /** 設置sign: 簽名,此處使用多次簽名 */
        putObjectRequest.setSign(bizService.getSign());

        /** 設置sliceFlag: 是否開啓分片上傳 */
        putObjectRequest.setSliceFlag(true);
        /** 設置slice_size: 若使用分片上傳,設置分片的大小 */
        putObjectRequest.setSlice_size(1024 * 1024);

        /** 設置sha: 是否上傳文件時帶上sha,一般帶上sha*/
        putObjectRequest.setSha(SHA1Utils.getFileSha1(localPath));

        /** 設置listener: 結果回調 */
        putObjectRequest.setListener(new IUploadTaskListener() {
            @Override
            public void onProgress(COSRequest cosRequest, long currentSize, long totalSize) {
                long progress = ((long) ((100.00 * currentSize) / totalSize));
                Log.w("XIAO", "progress =" + progress + "%");
            }

            @Override
            public void onCancel(COSRequest cosRequest, COSResult cosResult) {
                String result = "上傳出錯: ret =" + cosResult.code + "; msg =" + cosResult.msg;
                Log.w("XIAO", result);
            }

            @Override
            public void onSuccess(COSRequest cosRequest, COSResult cosResult) {
                PutObjectResult putObjectResult = (PutObjectResult) cosResult;
                StringBuilder stringBuilder = new StringBuilder();
                stringBuilder.append(" 上傳結果: ret=" + putObjectResult.code + "; msg =" + putObjectResult.msg + "\n");
                stringBuilder.append(" access_url= ");
                stringBuilder.append(putObjectResult.access_url == null ? "null" : putObjectResult.access_url + "\n");
                stringBuilder.append(" resource_path= ");
                stringBuilder.append(putObjectResult.resource_path == null ? "null" : putObjectResult.resource_path + "\n");
                stringBuilder.append(" url= ");
                stringBuilder.append(putObjectResult.url == null ? "null" : putObjectResult.url);
                String result = stringBuilder.toString();
                Log.w("XIAO", result);
            }

            @Override
            public void onFailed(COSRequest cosRequest, COSResult cosResult) {
                String result = "上傳出錯: ret =" + cosResult.code + "; msg =" + cosResult.msg;
                Log.w("XIAO", result);
            }
        });
        /** 發送請求:執行 */
        bizService.cosClient.putObject(putObjectRequest);
}

②文件下載

public void onDownload() {
        final String savePath = Environment.getExternalStorageDirectory().getAbsolutePath() +
                File.separator + "test_download";
        final String downloadUrl = "http://demo-1253703400.cosgz.myqcloud.com/bd_etts_text.dat";
        resultText.setText("download_url :" + downloadUrl + "\n" + "savePath :" + savePath);
        new Thread(new Runnable() {
            @Override
            public void run() {
                GetObject.getObject(bizService, downloadUrl, savePath);
            }
        }).start();
}

public static void getObject(BizService bizService, String url, String savePath){
        /** GetObjectRequest 請求對象 */
        GetObjectRequest getObjectRequest = new GetObjectRequest(url,savePath);
        //若是設置了防盜鏈則需要簽名;否則,不需要
        /** 設置listener: 結果回調 */
        getObjectRequest.setListener(new IDownloadTaskListener() {
            @Override
            public void onProgress(COSRequest cosRequest, long currentSize, long totalSize) {
                long progress = (long) ((100.00 * currentSize) / totalSize);
                Log.w("XIAO","progress =" + progress + "%");
            }

            @Override
            public void onCancel(COSRequest cosRequest, COSResult cosResult) {
                String result = "cancel =" + cosResult.msg;
                Log.w("XIAO",result);
            }

            @Override
            public void onSuccess(COSRequest cosRequest, COSResult cosResult) {
                String result = "code =" + cosResult.code + "; msg =" + cosResult.msg;
                Log.w("XIAO",result);
            }

            @Override
            public void onFailed(COSRequest cosRequest, COSResult cosResult) {
                String result ="code =" + cosResult.code + "; msg =" + cosResult.msg;
                Log.w("XIAO",result);
            }
        });
        bizService.cosClient.getObject(getObjectRequest);
}

注意:我們上傳的如果是中文名文件,服務器生成下載鏈接時會對中文進行轉碼,形成如%E9%之類的一串字符


如果用瀏覽器下載此鏈接地址,當然不會有問題,瀏覽器會自動轉碼

但是問題來了,如果調用騰訊雲API的cosClient.getObject(getObjectRequest)或者cosClient.getObjectAsyn(getObjectRequest)


騰訊這裏會調用OKhttp去執行下載任務,並不會幫我們實現轉碼,所以我們下載成功的文件是這樣的


解決方案就是:在下載成功的onSuccess回調裏將文件名轉碼回中文,方法如下

private static void renameFile(String url, String savePath) {
        String str = url.substring(url.lastIndexOf("/") + 1);
        StringBuilder localUrl = new StringBuilder(savePath);
        StringBuilder destUrl = new StringBuilder(savePath);
        String fileName = null;
        try {
            fileName = URLDecoder.decode(str, "utf-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        localUrl.append(File.separator).append(str);
        destUrl.append(File.separator).append(fileName);
        File file = new File(localUrl.toString());
        File destFile = new File(destUrl.toString());
        file.renameTo(destFile);
}

這估計是我目前最長的一篇博客了,博主比較懶沒啥耐心喜歡直接貼代碼吐舌頭,不怎麼喜歡細緻講解,所以還有一些功能就不一一羅列了,比如目錄查詢、文件查詢、文件更新、文件刪除、文件複製、文件移動,這些在我上傳的demo都有示例用法。有什麼錯誤歡迎留言指正,有問題也可以留言相互討論交流。


PS補充:官方的github鏈接https://github.com/tencentyun/cos_android_sdk/blob/master/README.md ,官方提供了兩套操作,一組同步的一組異步的。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章