Giter Club home page Giter Club logo

android-sdk's Introduction

Qiniu Resource Storage SDK for Android

@qiniu on weibo Software License Build Status codecov Latest Stable Version

演示代码

https://github.com/qiniudemo/qiniu-lab-android

安装

运行环境

Qiniu SDK 版本 最低 Android版本 依赖库版本
8.8.x Android 4.0+ okhttp 4+
8.7.x Android 4.0+ okhttp 4+
8.6.x Android 4.0+ okhttp 4+
8.5.x Android 4.0+ okhttp 4+
8.4.x Android 4.0+ okhttp 4+
8.3.2 Android 4.0+ okhttp 4+
8.3.1 Android 4.0+ okhttp 4+
8.3.0 Android 5.0+ okhttp 4+
8.2.x Android 5.0+ okhttp 4+
8.1.x Android 5.0+ okhttp 4+
8.0.x Android 5.0+ okhttp 4+
7.7.x Android 5.0+ okhttp 4+
7.6.x Android 5.0+ okhttp 4+
7.5.x Android 5.0+ okhttp 4+
7.4.6 Android 4.0+ okhttp 3.12.6
7.3.x Android 2.3+ okhttp 3.11.0
7.2.x Android 2.3+ okhttp 3+
7.1.x Android 2.3+ okhttp 2.6+
7.0.8,7.0.9 Android 2.2+ android-async-http 1.4.9
7.0.7 Android 2.2+ android-async-http 1.4.8

注意

  • 推荐使用最新版:8.8.0
  • 7.6.2 ~ 8.3.2 AndroidNetwork.getMobileDbm()可以获取手机信号强度,需要如下权限(API>=18时生效)
  <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
  • 从7.5.0开始增加了DNS预取和缓存策略,减少dns解析错误
  • 如果可以明确 区域 的话,最好指定固定区域,这样可以少一步网络请求,少一步出错的可能。
  • 如果使用 Android 4.x ,对应 okhttp 版本请调整至 3.12.+

直接安装

将sdk jar文件 复制到项目中去,jar包下载地址 , 下载对应的jar包,以及搜索下载对应的依赖库

happy-dns下载地址

通过maven

  • Android Studio中添加dependencies 或者 在项目中添加maven依赖
// 1. 直接导入
implementation 'com.qiniu:qiniu-android-sdk:8.8.+'

// 2. 如果要修改okhttp依赖的版本,可采用以下方式(强烈建议使用七牛库依赖的okhttp版本)
implementation ('com.qiniu:qiniu-android-sdk:8.8.+'){
    exclude (group: 'com.squareup.okhttp3', module: 'okhttp')
}
implementation 'com.squareup.okhttp3:okhttp:4.9.1'

  • 如果是eclipse, 也可以直接添加依赖来处理。

使用方法

UploadManager 可以创建一次,一直使用。 7.6.2 ~ 8.3.2 会调用AndroidNetwork.getMobileDbm可以获取网络信号强度 需要Manifest.permission.ACCESS_FINE_LOCATION和Manifest.permission.ACCESS_COARSE_LOCATION权限

import com.qiniu.android.storage.UploadManager;
...
String token = "从服务端SDK获取";
UploadManager uploadManager = new UploadManager();
uploadManager.put("Hello, World!".getBytes(), "hello", token,
new UpCompletionHandler() {
    @Override
    public void complete(String key, ResponseInfo info, JSONObject response) {
        LogUtil.i(info);
    }
}, null);
...

支持使用 HTTP/3 协议发起请求

导入 HTTP/3 client 插件,http3 client 插件依赖于 Android SDK v8.5.0及以上版本

安装导入

// 移除 qiniu-android-sdk 依赖 : implementation 'com.qiniu:qiniu-android-sdk:x.x.+' 
implementation 'com.qiniu:qiniu-android-curl-plugin:1.0.0'

使用 CurlClient

import com.qiniu.client.curl.CurlClient;
import com.qiniu.android.storage.Configuration;
import com.qiniu.android.storage.UploadManager;

// @param caPath: SSL 证书本地路径;如果想自定义 CA 可设置此选项,此处为 CA 文件的本地路径。
// 				  如果未定义(caPath 配置 null)则使用 SDK 内部提供的 CA 证书,证书来源:https://curl.se/ca/cacert.pem
CurlClient client = new CurlClient(caPath);
Configuration config = new Configuration.Builder()
                .requestClient(client)
                .build();
UploadManager manager = new UploadManager(config);

测试

$ ./gradlew connectedAndroidTest

常见问题

1).有关Android Studio以及Eclipse安装运行Android Demo步骤,这里以Android Studio为实例: 1.修改 build.gradle 双击打开您的工程目录下的build.gradle,在dependencies中添加一条依赖compile 'com.qiniu:qiniu-android-sdk:7.2.+',如下所示:

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.1.1'
    compile 'com.qiniu:qiniu-android-sdk:7.6.+'
}

当然也可以将jar包下载到本地导入到项目中

2.添加相关权限,在 app/src/main 目录中的 AndroidManifest.xml 中增加如下 uses-permission 声明

 <uses-permission android:name="android.permission.INTERNET"/>
 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

3.布局,在res/layout/activity_main.xml添加相应的上传的按钮以及相关控件,以下以一个简单的布局为例:

<TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:textSize="20sp"
        android:layout_marginTop="10dp"
        android:text="七牛云存储 SDK"
        android:id="@+id/textView"
        android:layout_alignParentTop="true"
        />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="上传"
        android:id="@+id/button"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"/>

效果如下: 这里写图片描述

4.逻辑代码, 为按钮添加上传事件,这里以上传一个byte数组为例,另外,上传的数据可以是文件路径或者文件,以下给出具体的代码:

package com.dxy.cloud.myapplication;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.qiniu.android.http.ResponseInfo;
import com.qiniu.android.storage.UpCompletionHandler;
import com.qiniu.android.storage.UploadManager;
import com.qiniu.android.storage.UploadOptions;
import org.json.JSONObject;

public class MainActivity extends Activity implements View.OnClickListener {

    //指定upToken, 强烈建议从服务端提供get请求获取, 这里为了掩饰直接指定key
    public static String uptoken = "xxxxx:xxxxx:xxxxx";
    private Button btnUpload;
    private TextView textView;
    private UploadManager uploadManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) findViewById(R.id.textView);
        btnUpload = (Button) findViewById(R.id.button);
        btnUpload.setOnClickListener(this);
        //new一个uploadManager类
        uploadManager = new UploadManager();

    }

    @Override
    public void onClick(View v) {
        LogUtil.i("starting......");
        byte[] data=new byte[]{ 0, 1, 2, 3};
        //设置上传后文件的key
        String upkey = "uploadtest.txt";
        uploadManager.put(data, upkey, uptoken, new UpCompletionHandler() {
            public void complete(String key, ResponseInfo rinfo, JSONObject response) {
                btnUpload.setVisibility(View.INVISIBLE);
                String s = key + ", " + rinfo + ", " + response;
                LogUtil.i(s);
                textView.setTextSize(10);
                String o = textView.getText() + "\r\n\r\n";
                //显示上传后文件的url
                textView.setText(o + s + "\n" + "http://xm540.com1.z0.glb.clouddn.com/" + key);
            }
        }, new UploadOptions(null, "test-type", true, null, null));

    }
}

运行的效果图如下: 这里写图片描述

2).有关断点续传,暂停上传,设置自定义变量的用法,这里以一个简单的Demo给出下实现的方法,这里的Demo主要实现从相册选择一张图片上传来说明。

可以参考放GitHub(Android Studio)上的源码: https://github.com/clouddxy/AndroidDemo 这里直接给出MainActivity中的代码:

package com.dxy.cloud.myapplication;

import android.app.Activity;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.qiniu.android.http.ResponseInfo;
import com.qiniu.android.storage.Configuration;
import com.qiniu.android.storage.KeyGenerator;
import com.qiniu.android.storage.Recorder;
import com.qiniu.android.storage.UpCancellationSignal;
import com.qiniu.android.storage.UpCompletionHandler;
import com.qiniu.android.storage.UpProgressHandler;
import com.qiniu.android.storage.UploadManager;
import com.qiniu.android.storage.UploadOptions;
import com.qiniu.android.storage.persistent.FileRecorder;
import com.qiniu.android.utils.UrlSafeBase64;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;

public class MainActivity extends Activity {
    private Button button1;
    private Button button2;
    private Button button3;
    private ImageView imageview;
    private Uri imageUri;
    private TextView textview;
    private ProgressBar progressbar;
    public static final int RESULT_LOAD_IMAGE = 1;
    private volatile boolean isCancelled = false;
    UploadManager uploadManager;
    public MainActivity() {
        //断点上传
        String dirPath = "/storage/emulated/0/Download";
        Recorder recorder = null;
        try{
            File f = File.createTempFile("qiniu_xxxx", ".tmp");
            LogUtil.d(f.getAbsolutePath().toString());
            dirPath = f.getParent();
            //设置记录断点的文件的路径
            recorder = new FileRecorder(dirPath);
        } catch(Exception e) {
            e.printStackTrace();
        }

        final String dirPath1 = dirPath;
        //默认使用 key 的url_safe_base64编码字符串作为断点记录文件的文件名。
        //避免记录文件冲突(特别是key指定为null时),也可自定义文件名(下方为默认实现):
        KeyGenerator keyGen = new KeyGenerator(){
            public String gen(String key, File file){
                // 不必使用url_safe_base64转换,uploadManager内部会处理
                // 该返回值可替换为基于key、文件内容、上下文的其它信息生成的文件名
                String path = key + "_._" + new StringBuffer(file.getAbsolutePath()).reverse();
                LogUtil.d(path);
                File f = new File(dirPath1, UrlSafeBase64.encodeToString(path));
                BufferedReader reader = null;
                try {
                    reader = new BufferedReader(new FileReader(f));
                    String tempString = null;
                    int line = 1;
                    try {
                        while ((tempString = reader.readLine()) != null) {
//							System.out.println("line " + line + ": " + tempString);
                            LogUtil.d("line " + line + ": " + tempString);
                            line++;
                        }

                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    } finally {
                        try{
                            reader.close();
                        } catch(Exception e) {
                            e.printStackTrace();
                        }
                    }
                } catch (FileNotFoundException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                return path;
            }
        };

        Configuration config = new Configuration.Builder()
                // recorder 分片上传时,已上传片记录器
                // keyGen 分片上传时,生成标识符,用于片记录器区分是那个文件的上传记录
                .recorder(recorder, keyGen)
                .build();
        // 实例化一个上传的实例
        uploadManager = new UploadManager(config);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        button1 = (Button) findViewById(R.id.bt1);
        button2 = (Button) findViewById(R.id.bt2);
        button3 = (Button) findViewById(R.id.bt3);
        imageview = (ImageView) findViewById(R.id.iv);
        textview = (TextView) findViewById(R.id.tv);
        progressbar = (ProgressBar) findViewById(R.id.pb);

        // final String token = edittext.getText().toString();

        button1.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent i = new Intent(
                        Intent.ACTION_PICK,
                        android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);

                startActivityForResult(i, RESULT_LOAD_IMAGE);

            }
        });
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == RESULT_LOAD_IMAGE && resultCode == RESULT_OK
                && data != null) {
            Uri selectedImage = data.getData();
            String[] filePathColumn = { MediaStore.Images.Media.DATA };

            Cursor cursor = getContentResolver().query(selectedImage,
                    filePathColumn, null, null, null);
            cursor.moveToFirst();

            int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
            final String picturePath = cursor.getString(columnIndex);
            LogUtil.d(picturePath);
            cursor.close();

            imageview.setVisibility(View.VISIBLE);
            imageview.setImageBitmap(BitmapFactory.decodeFile(picturePath));

            //自定义参数returnbody
            //"returnBody":"{\"key\":$(key),\"hash\":$(etag),\"fname\":$(fname),\"phone\":$(x:phone)}
            final String token = "xxxxx";
            button2.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {

					//设定需要添加的自定义变量为Map<String, String>类型 并且放到UploadOptions第一个参数里面
                    HashMap<String, String> map = new HashMap<String, String>();
                    map.put("x:phone", "12345678");

                    LogUtil.d("click upload");
                    isCancelled = false;
                    uploadManager.put(picturePath, null, token,
                            new UpCompletionHandler() {
                                public void complete(String key,
                                                     ResponseInfo info, JSONObject res) {
                                    LogUtil.i(key + ",\r\n " + info
                                            + ",\r\n " + res);

                                    if(info.isOK()==true){
                                        textview.setText(res.toString());
                                    }
                                }
                            }, new UploadOptions(map, null, false,
                                    new UpProgressHandler() {
                                        public void progress(String key, double percent){
                                            LogUtil.i(key + ": " + percent);
                                            progressbar.setVisibility(View.VISIBLE);
                                            int progress = (int)(percent*1000);
//											Log.d("qiniu", progress+"");
                                            progressbar.setProgress(progress);
                                            if(progress==1000){
                                                progressbar.setVisibility(View.GONE);
                                            }
                                        }

                                    }, new UpCancellationSignal(){
                                @Override
                                public boolean isCancelled() {
                                    return isCancelled;
                                }
                            }));
                }
            });

            button3.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    isCancelled = true;
                }
            });
        }
    }
}

上传过程进度条: 这里写图片描述 上传后自定义变量回调: 这里写图片描述

3).关于从服务器获取Token上传: Android SDK为客户端SDK,没有包含token生成实现,为了安全,token都建议通过网络从服务端获取,具体生成代码可以参考"java/python/php/ruby/go"等服务端sdk, 其实也比较简单,可以使用okhttp发送一个简单的get请求就可以了,这里以使用SyncHttpClient为例来说明:

package com.example.androidupload;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.loopj.android.http.SyncHttpClient;
import com.loopj.android.http.TextHttpResponseHandler;
import com.qiniu.android.storage.UpCompletionHandler;
import com.qiniu.android.storage.UpProgressHandler;
import com.qiniu.android.storage.UploadManager;
import com.qiniu.android.storage.UploadOptions;
import com.qiniu.android.utils.UrlSafeBase64;
import org.apache.http.Header;
import org.json.JSONObject;
import java.io.File;
import java.util.UUID;

public class MainActivity extends Activity {

    private UploadManager uploadManager;
    private final String tag = "MainActivity";

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //设置本地上传的文件的路径
        String path ="/storage/emulated/0/Download/DSC_0770.JPG";
        String aaa = UrlSafeBase64.encodeToString(path);
        File file =new File(path);
    }

    private void upload(final String localPath,final String key)
    {
        Thread thread = new Thread()
        {
            @Override
            public void run() {
                super.run();
                SyncHttpClient client = new SyncHttpClient();
                //获取服务端的token的url
                String url = "http://xxxx/xxxx/xxx";
                client.get(url, new TextHttpResponseHandler() {
                    @Override
                    public void onFailure(int i, Header[] headers, String s, Throwable throwable) {
                    }
                    @Override
                    public void onSuccess(int i, Header[] headers, String s) {
                        LogUtil.i("请求七牛token:"+s);
                        JsonParser jsonParser = new JsonParser();
                        JsonElement jsonElement =jsonParser.parse(s);
                        String token = jsonElement.getAsJsonObject().get("uptoken").toString();
                        LogUtil.i("七牛开始上传"+localPath+"\n"+key+"\n"+token);
                        if(uploadManager==null)
                        {
                            uploadManager = new UploadManager();
                        }
                        UploadOptions uploadOptions = new UploadOptions(null, null, false,
                                new UpProgressHandler() {
                                    @Override
                                    public void progress(String key, double percent) {
                                        LogUtil.i("a 七牛上传progress:"+percent+"\n"+key);
                                    }
                                }, null);

                        //调用uploadManager上传
                        uploadManager.put(localPath, key, token, new UpCompletionHandler() {
                            @Override
                            public void complete(String key, com.qiniu.android.http.ResponseInfo info, JSONObject response) {
                                LogUtil.i("a 七牛上传complete:"+key + ",\r\n " + info + ",\r\n " + response);
                            }
                        },uploadOptions);
                    }
                });
            }
        };
        thread.start();
    }
}

4).关于for循环上传: 可以参考demo:

package com.dxy.cloud.myapplication;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.qiniu.android.http.ResponseInfo;
import com.qiniu.android.storage.UpCompletionHandler;
import com.qiniu.android.storage.UploadManager;
import com.qiniu.android.storage.UploadOptions;
import org.json.JSONObject;

public class MainActivity extends Activity implements View.OnClickListener {

    //指定upToken, 强烈建议从服务端提供get请求获取, 这里为了掩饰直接指定key
    public static String uptoken = "xxxxxxxxx:xxxxxxx:xxxxxxxxxx";
    private Button btnUpload;
    private TextView textView;
    private UploadManager uploadManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) findViewById(R.id.textView);
        btnUpload = (Button) findViewById(R.id.button);
        btnUpload.setOnClickListener(this);
        //new一个uploadManager类
        uploadManager = new UploadManager();
    }

    @Override
    public void onClick(View v) {
        LogUtil.i("starting......");
        byte[] data=new byte[]{ 0, 1, 2, 3, 3, 4, 5, 6,0, 1, 2, 3, 4, 5, 6,0, 1, 2, 3, 4, 5, 6,0, 1, 2, 3, 4, 5, 6,0, 1, 2, 3, 4, 5, 6,0, 1, 2, 3,};

       for(int i=0;i<data.length;i++){
        	String expectKey =UUID.randomUUID().toString();
            uploadManager.put(data, expectKey, uptoken, new UpCompletionHandler() {
                public void complete(String k, ResponseInfo rinfo, JSONObject response) {
                    String s = k + ", "+ rinfo + ", " + response;
                    LogUtil.i(s);
                    String key = getKey(k, response);
                    String o = hint.getText() + "\r\n\r\n";
                    hint.setText(o + s + "http://xm540.com1.z0.glb.clouddn.com/" + key);
                }
            }, new UploadOptions(null, "test-type", true, null, null));
        }
    }
}

5).常见的返回的状态码: 使用七牛 Android sdk 的时候, 如果使用方式不正确,会返回一些对应的错误码。 具体可以参考源码: https://github.com/qiniu/android-sdk/blob/c4cd1437aa1f2a0d68122e46b83580facdf1b74a/library/src/main/java/com/qiniu/android/http/ResponseInfo.java

public static final int MaliciousResponseError = -8;
public static final int LocalIOError = -7;
public static final int ZeroSizeFile = -6;
public static final int InvalidToken = -5;
public static final int InvalidArgument = -4;
public static final int InvalidFile = -3;
public static final int Cancelled = -2;
public static final int NetworkError = -1;
public static final int TimedOut = -1001;
public static final int UnknownHost = -1003;
public static final int CannotConnectToHost = -1004;
public static final int NetworkConnectionLost = -1005;

6).关于混淆配置:

混淆处理 对七牛的SDK不需要做特殊混淆处理,混淆时将七牛相关的包都排除就可以了。

在Android Studio中,混淆配置在 proguard-rules.pro 里面加上下面几行混淆代码就行:

-keep class com.qiniu.**{*;}
-keep class com.qiniu.**{public <init>();}
-ignorewarnings

注意:-ignorewarnings这个也是必须加的,如果不加这个编译的时候可能是可以通过的,但是release的时候还是会出现错误。

对于Eeclipse中也是一样的,在proguard-project.txt 文件附件同样的排除代码就可以了

-keep class com.qiniu.**{*;}
-keep class com.qiniu.**{public <init>();}
-ignorewarnings

7).为什么进度会在95% 停很久: 因为上传进度是用sdk写入socket的 字节数/总字节数 作为进度,但写入socket不等于服务器收到并且处理完成,中间还有一段时间,如果只是用字节数就会出现更怪异的情况,在100% 停留很久,所以综合考虑就使用了 95%这个值.

8).如何对文件名字做模糊查询: 如果前缀查询满足不了需求,想做多维度的查询,比如日期,userid, 文件名中的字段进行查询,可以通过开通Pandora 日志查询服务来满足。下面的例子里面需要在工作流上创建key, user, etag, mime, 四个字段的repo, 具体客户端做法如下,在上传结束后,再把结果打点到pandora 服务

import com.qiniu.android.storage.UploadManager;
import com.qiniu.android.bigdata.pipeline.Pipeline;
...
    String token = "从服务端SDK获取";
    UploadManager uploadManager = new UploadManager();
    String pipelineToken = "从服务端获取 或者 生成一个长时间的token";
    Pipeline pipe = new Pipeline();
    uploadManager.put("Hello, World!".getBytes(), "hello", token,
    new UpCompletionHandler() {
        @Override
        public void complete(String key, ResponseInfo info, JSONObject response) {
            Map<String, Object> inf = new HashMap<String, Object>();
            inf.put("key", key);
            inf.put("user", "userid");
            inf.put("etag", resp.getString("hash"));
            inf.put("mime", resp.getString("mimeType"));
            pipe.pump("keysearchRepo", inf, pipelineToken, new Pipeline.PumpCompleteHandler() {
                @Override
                public void complete(ResponseInfo inf2) {
                    ...
                }
            });
        }
    }, null);
...

9).如何理解上传返回 code : 详情 code 注释说明

代码贡献

详情参考代码提交指南

贡献记录

联系我们

  • 如果需要帮助,请提交工单(在portal右侧点击咨询和建议提交工单,或者直接向 [email protected] 发送邮件)
  • 如果有什么问题,可以到问答社区提问,问答社区
  • 更详细的文档,见官方文档站
  • 如果发现了bug, 欢迎提交 issue
  • 如果有功能需求,欢迎提交 issue
  • 如果要提交代码,欢迎提交 pull request
  • 欢迎关注我们的微信 微博,及时获取动态信息。

代码许可

The MIT License (MIT).详情见 License文件.

android-sdk's People

Contributors

bachue avatar bluntblade avatar carlji avatar carter2000 avatar chzyer avatar clouddxy avatar e06084 avatar hamberluo avatar hantuo avatar haoxiqiang avatar hughlv avatar jemycheung avatar jemygraw avatar lidaobing avatar longbai avatar longshanksmo avatar lyyyuna avatar mei-zhao avatar songfei9315 avatar sxci avatar wangjinlei avatar why404 avatar xushiwei avatar xuzhaokui avatar xwen-winnie avatar yangsen-qn avatar zshbleaker avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

android-sdk's Issues

找不到HttpManager?! HttpManager类型是.class

错误信息:
16:32:15.853 [ERROR] [system.err] Warning: com.qiniu.android.http.HttpManager$3: can't find referenced class com.qiniu.android.http.HttpManager

依赖:
compile 'com.qiniu:qiniu-android-sdk:7.1.2'

能否加入同步的上传方法?

现在只有异步的上传方法。

通过UploadOptions回调,能否加入同步的上传,一直等待其结束或者出错才返回。

七牛图片上传任务只是整个任务的一部分,我希望在一个自己开的线程里面完成整个任务(七牛图片上传任务只是一部分)。

这样子可以简化线程同步的问题。

估计这种应用场合很多人会碰到吧?

建议增加同步上传功能

建议增加同步上传功能,因为后台线程并不只是上传数据到七牛服务器,还要上传数据到自己的服务器。
现在使用异步上传,需要:
1、一个线程从自己服务器获取上传凭证;
2、利用上传凭证上传数据到七牛服务器;
3、再开一个线程上传七牛返回的数据到自己的服务器。
如果使用同步上传的话,那么只需要一个线程就够了。

有删除功能吗

例如替换某张图片,需要将之前的替换或者直接删除

UploadManager 的post方法是支持在非ui线程调用的吗?

hello,我在com.qiniu.android.storage.UploadManager类的源码里面看到这样的一段代码,
public void put(File file, final String key, String token, final UpCompletionHandler completionHandler,
final UploadOptions options) {
if (areInvalidArg(key, null, file, token, completionHandler)) {
return;
}
UpToken decodedToken = UpToken.parse(token);
if (decodedToken == null) {
final ResponseInfo info = ResponseInfo.invalidToken("invalid token");
AsyncRun.run(new Runnable() {
@OverRide
public void run() {
completionHandler.complete(key, info, null);
}
});
return;
}
long size = file.length();
if (size <= config.putThreshold) {
FormUploader.upload(httpManager, config, file, key, decodedToken, completionHandler, options);
return;
}
String recorderKey = config.keyGen.gen(key, file);
ResumeUploader uploader = new ResumeUploader(httpManager, config, file, key,
decodedToken, completionHandler, options, recorderKey);

    AsyncRun.run(uploader);
}

在这个代码里面,如果是文件大小小与配置的文件大小,则使用表单上传,否则使用分片上传,但是在这里的分片上传是使用了AsyncRun,然后在ui线程跑,然后表单上传却是直接调用。
不知道我的理解有没有错,如果没有错的话,这个是基于什么样的考量呢?

UpProgressHandler#progress的行为不确定

文档里提到UpProgressHandler#progress会运行在主线程,可是,当我同时上传几个文件(共用一个UploadManager),这个时候,回调偶尔在主线程,偶尔不在主线程

is Main Loop  : false
is Main Loop  : false
is Main Loop  : false
is Main Loop  : false
is Main Loop  : true
is Main Loop  : false
is Main Loop  : false
is Main Loop  : false
is Main Loop  : false
is Main Loop  : false
is Main Loop  : false
is Main Loop  : true

这个log是我在progress里面打的

sdk版本7.0.3
async http版本 1.4.6

可能与rxjava冲突?

E/dalvikvm: Could not find class 'rx.subjects.SubjectSubscriptionManager', referenced from method rx.subjects.PublishSubject.create
W/dalvikvm: VFY: unable to resolve new-instance 8872 (Lrx/subjects/SubjectSubscriptionManager;) in Lrx/subjects/PublishSubject;

一引用你们的sdk就报这个错误,然后应用就崩溃了。
下面是我引用的一些库:

compile 'com.android.support:multidex:1.0.1'
// https://github.com/google/gson
compile 'com.google.code.gson:gson:2.6.2'
// https://github.com/JakeWharton/butterknife
compile 'com.jakewharton:butterknife:7.0.1'
// https://github.com/orfjackal/retrolambda
retrolambdaConfig 'net.orfjackal.retrolambda:retrolambda:2.1.0'
// https://github.com/greenrobot/EventBus
compile 'org.greenrobot:eventbus:3.0.0'
// https://github.com/ReactiveX/RxJava
compile 'io.reactivex:rxjava:1.1.0'
// https://github.com/ReactiveX/RxAndroid
compile 'io.reactivex:rxandroid:1.1.0'
// https://projectlombok.org/
compile 'org.projectlombok:lombok:1.16.8'
// https://github.com/loopj/android-async-http
compile 'com.loopj.android:android-async-http:1.4.9'
// https://github.com/qiniu/android-sdk
compile 'com.qiniu:qiniu-android-sdk:7.2.+'

7.0.9版的例子里,断点续传,一点击暂停后就提示报错,断点存储文件不存在,是我这边配置错了还是怎么回事呢?

红米2手机 已经 模拟器都是这样的错,用命令行进去那个目录,确实有那么一个文件的!报错的提示是:java.io.FileNotFoundException: /data/data/com.qiniu.qiniulab/files/QiniuAndroid/Mzc3TkFrRnpjNDVRampYQ0FGYWJ5SWxVZlVNPS5wcm9ncmVzcw==: open failed: ENOENT (No such file or directory)

实际上有那么一个文件,是不是 -rw------ 这个权限的问题

红米2手机进不去那个目录,提示没权限

ResumeUploader的RandomAccessFile file 使用后未关闭

若使用以下代码开启StrictMode, 会在文件上传后抛出异常,应用退出,提示相应的RandomAccessFile未关闭。

 if (BuildConfig.DEBUG && Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                    .detectDiskReads()
                    .detectDiskWrites()
                    .detectNetwork() // or .detectAll() for all detectable   problems
                    .penaltyLog()
                    .build());
            StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
                    .detectLeakedSqlLiteObjects()
                    .detectLeakedClosableObjects()
                    .penaltyLog()
                    .penaltyDeath()
                    .build());
        }

异常:

08-26 14:57:50.913  27269-27281/com.meiyaapp.meiya E/StrictMode﹕ A resource was acquired at attached stack trace but never released. See java.io.Closeable for information on avoiding resource leaks.
    java.lang.Throwable: Explicit termination method 'close' not called
            at dalvik.system.CloseGuard.open(CloseGuard.java:184)
            at java.io.RandomAccessFile.<init>(RandomAccessFile.java:127)
            at com.qiniu.android.storage.ResumeUploader.run(ResumeUploader.java:78)
            at android.os.Handler.handleCallback(Handler.java:739)
            at android.os.Handler.dispatchMessage(Handler.java:95)
            at android.os.Looper.loop(Looper.java:135)
            at android.app.ActivityThread.main(ActivityThread.java:5254)
            at java.lang.reflect.Method.invoke(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:372)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
08-26 14:57:50.913  27269-27281/com.meiyaapp.meiya W/System.err﹕ StrictMode VmPolicy violation with POLICY_DEATH; shutting down.

将StrictMode的开启代码移除后,应用就不会直接退出了,但提示的RandomAccessFile未关闭,应该是存在的。 我大致看了下com.qiniu.android.storage.ResumeUploader的代码,貌似确实没有关闭代码。
这个应该是要处理下的, 否则文件上传后,相应的文件对象都没有关闭,长期操作会不会对应用产生影响。

什么时候加同步上传方式

什么时候加同步上传方式,什么时候加同步上传方式,什么时候加同步上传方式。现在只有异步方式上传。

疑似bug反馈

sdk中对于分片上传的片大小配置,如果片大小不能够整除块大小的话(即 BLOCK_SIZE % CHUNK_SIZE != 0),可能会出现read exceed block size的错误。
引起该问题的代码为
/library/src/main/java/com/qiniu/android/storage/ResumeUploader.java 中的

private int calcPutSize(int offset) {
int left = size - offset;
return left < Config.CHUNK_SIZE ? left : Config.CHUNK_SIZE;
}
如果配置中BLOCK_SIZE % CHUNK_SIZE != 0的话,每块的最后一片大小也需要考虑,而不仅仅是整个文件的最后一片大小。

不知是否是个bug

AndroidStuidio 打包 引入七牛sdk的项目 失败

打包Release版本时出现下面的警告导致打包失败

Warning:com.qiniu.android.http.HttpManager$3: can't find referenced method 'com.qiniu.android.http.IReport access$100(com.qiniu.android.http.HttpManager)' in program class com.qiniu.android.http.HttpManager

ResumeUploader#nextTask中为什么进度最大只会到0.95?

final int chunkSize = calcPutSize(offset);
        ProgressHandler progress = new ProgressHandler() {
            @Override
            public void onProgress(int bytesWritten, int totalSize) {
                double percent = (double) (offset + bytesWritten) / size;
                if (percent > 0.95) {
                    percent = 0.95;
                }
                options.progressHandler.progress(key, percent);
            }
        };

同样的token在GO版本上可以,在Android版本上显示无效的token

同样的token和key在GO的Client上可以上传文件,但是使用Android-SDK就显示bad token。

// go版本的代码
func main() {
    zone := 0
    uploader := kodocli.NewUploader(zone, nil)
    ctx := context.Background()
    uptoken := "U4PsY2wIJoRilzo10kcZyw9V9BRfB6KFKVCBAv9r:JNub0H_mJiNcrx1IN1lnfa4s5EU=:eyJzY29wZSI6InFuaXU6dGVzdC9hNTIwZGUwZjY0ZmM3NGMxOTRhYWU0Y2NkMTE1YjA3NS5qcGciLCJkZWFkbGluZSI6MTQ0OTc2Nzk4OH0="

    key := "test/a520de0f64fc74c194aae4ccd115b075.jpg"
    localFile := "/home2/nferzhuang/qiniu/src/client/button.png"
  var ret kodocli.PutRet
    err := uploader.PutFile(ctx, &ret, uptoken, key, localFile, nil)
    if err != nil {
        fmt.Println("put file failed")
        return
    }

    fmt.Println("put file OK, ret:", ret)
}

Android版本的代码:

            public void onClick(View v) {
                if (uploadManager == null) {
                    uploadManager = new UploadManager();
                }

                resultText.setText("Uploading... Please wait");
                String key = username + "/" + filename;
                Log.i("qiniu", "key:" + key);
                Log.i("qiniu", "uptoken:" + uptoken);
                File uploadFile = new File(filePath);
                uploadManager.put(uploadFile, key, uptoken, new UpCompletionHandler() {
                    @Override
                    public void complete(String key, ResponseInfo info, JSONObject response) {
                        Log.i("qiniu", info.toString());
                        resultText.setText(info.toString());
                    }
                }, null);
            }

其中我通过打印的方式,确认了key和token是一致的。
而且,我在生成token的时候设置的有效期是36000(10个小时);
并且,我测试GO的Client上传可以;再测试Android版本,显示无效的token;回到GO版本,仍然是可以的。

和 multidex 一起使用会有问题

Error:Execution failed for task ':app:packageAllDebugClassesForMultiDex'.

java.util.zip.ZipException: duplicate entry: com/loopj/android/http/AsyncHttpClient$1.class

最新的 7.0.9 版本。

适配6.0

什么时候能适配6.0啊,使用七牛导致我的应用都没法适配6.0了~~~

loopj 1.4.9 remove apache jar

Warning: com.qiniu.android.http.HttpManager: can't find referenced method 'void setRedirectHandler(org.apache.http.client.RedirectHandler)' in program class com.qiniu.android.http.AsyncHttpClientMod
Warning: com.qiniu.android.http.HttpManager: can't find referenced method 'com.loopj.android.http.RequestHandle post(android.content.Context,java.lang.String,org.apache.http.Header[],org.apache.http.HttpEntity,java.lang.String,com.loopj.android.http.ResponseHandlerInterface)' in program class com.qiniu.android.http.AsyncHttpClientMod
Warning: com.qiniu.android.http.HttpManager$2: can't find referenced method 'com.loopj.android.http.RequestHandle post(android.content.Context,java.lang.String,org.apache.http.Header[],org.apache.http.HttpEntity,java.lang.String,com.loopj.android.http.ResponseHandlerInterface)' in program class com.qiniu.android.http.AsyncHttpClientMod
Warning: there were 3 unresolved references to program class members.

A terrible problem!

提供直接提交的方法,返回固定json格式,开发者自己解析

不知道你们考虑过普通的一个上传流程是怎样的没有?先从自己服务器上获取token,然后切换到主线程调用你们的api,再通过回调又切到主线程拿到结果,然后再开线程把key上传到自己的服务器,获取完整URL。整个流程本来只是一件事,完全可以封装到一个线程里。而因为你们的设计完全打破了。代码凌乱不堪。还要另加一个网络库....同时,一次提交根本无法提交多个文件,如果遇到需要上传多张图片的场景,这场景太美,我都不敢想象~希望能够直接提供一个上传api。解释清楚入参,出参。

`saveKey` not work

My putPolicy is:

    scope: CONFIG.qiniu.scope
    deadline: Math.floor(nextMonth.getTime() / 1000)
    endUser: "#{ appId }/#{ userId }"
    saveKey: '$(endUser)/$(x:targetId)/$(x:timestamp)'
    returnBody: '{\"key\":$(key)}'
    detectMime: 1

Android code, I use SDK 7.0.3:

    UploadManager manager = new UploadManager();
    Map<String, String> params = new HashMap<String, String>();
    params.put("x:targetId", message.getRecipient());
    params.put("x:timestamp", getTimestampString(new Date()));
    final UploadOptions opt = new UploadOptions(params, null, true, null, null);
    manager.put(filePath, null, token, new UpCompletionHandler() {

        @Override
        public void complete(String key, ResponseInfo info, JSONObject responseObject) {
            LogUtil.info(LogUtil.LOG_TAG_CHAT, "Upload file finished with key: " + key + ", info: " + info);
        }
    }, opt);

I can successfully upload image, but there is no key return, below is the log message:

Upload file finished with key: null, info: {ResponseInfo:com.qiniu.android.http.ResponseInfo@3e1293b1,status:200, reqId:wRIAAFC1CzaRn9cT, xlog:s.ph;s.ph;s.ph;PFDS;PFDS;PFDS:1;PTFDM;PFDT;s.gh;PFDS/613;PTFDM:1;PTFDS;rdb.g/no such key;DBD/404;DBG/404;v4.exist:1/Document not found;rs12_6.ins:1;qtbl.ins:2;mc.s;RS:3;rs.put:4;UP:14, xvia:null,  host:upload.qiniu.com, ip:null, duration:0.034000 s, error:null}

The JavaScript SDK can get key, Where do I wrong?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.