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 ,官方提供了两套操作,一组同步的一组异步的。



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