Androidネットワークオープンソースライブラリ-Retrofit(3)一括アップロードおよびアップロード進捗リスニング

25748 ワード

  • gif図が大きすぎるためgithubに図を置きましたが、ブログに図が表示されなければ、転送ゲート
  • 私は事前にmdに書いたので、コードの可読性が悪いので、皆さんは見てみましょう.

  • 1.はじめに
    前回のブログでは、Retrofitのファイルアップロード、ファイルダウンロード、および進捗リスニングについて説明しましたが、このブログでは、次の一括アップロードおよびアップロードの進捗のリスニングについて説明します.
    2.一括アップロード
    一括アップロードを実現するには、HTMLで一括アップロードを実現する方法を考え、Formフォームを利用するので、Formフォームを利用して一括アップロードを実現することもできます.
    2.1 HTML FORMフォームの書き方
    <html>
    <body>
    
    <form action="http://localhost/fileabout.php" enctype="multipart/form-data" method="post">
      <p>First name: <input type="file" name="file[]" id="name1" />p>
      <p>First name: <input type="file" name="file[]" id="name2" />p>
      <p>First name: <input type="file" name="file[]" id="name3" />p>
      <input type="submit" value="Submit" />
    form>
    
    body>
    html>
  • action formフォーム発行アドレス
  • enctypeはフォームを符号化する方法を示し、multipart/form-dataはfile
  • があることを示す.
  • inputラベルのnameはxxx[]のフォーマットでなければなりません.配列の要素であることを示します(ps:正しいかどうか分かりませんが、[]を削除するとphpが受信できません)
  • 2.2 php受信コード
       
        header('Content-Type:text/html;charset=utf-8');
        $fileArray = $_FILES['file'];//         ,  :        []
    
        $upload_dir = "D:\WWW"."\\"; //         
        foreach ( $fileArray['error'] as $key => $error) {
            if ( $error == UPLOAD_ERR_OK ) { //PHP  UPLOAD_ERR_OK=0,        
                $temp_name = $fileArray['tmp_name'][$key];
                $file_name = $fileArray['name'][$key];
                move_uploaded_file($temp_name, $upload_dir.$file_name);
                echo '  [  '.$file_name.']  !
    '
    ; }else { echo ' [ '.$key.'] !
    '
    ; } }
  • すべてのファイルに$が存在します.FILESグローバル変数のうち、複数のファイルの場合、フォーマットは以下の
  • である.
     array(1) {
             ["file"]=> array(5) {
                       ["name"]=> array(3) { 
                                [0]=> string(5) "1.txt"
                                [1]=> string(5) "2.txt"
                                [2]=> string(5) "3.txt" }
             ["type"]=> array(3) {
                               [0]=> string(10) "text/plain"
                               [1]=> string(10) "text/plain" 
                               [2]=> string(10) "text/plain" } 
             ["tmp_name"]=> array(3) { 
                              [0]=> string(27) "C:\Windows\Temp\phpB829.tmp"
                              [1]=> string(27) "C:\Windows\Temp\phpB82A.tmp" 
                              [2]=> string(27) "C:\Windows\Temp\phpB82B.tmp" } 
            ["error"]=> array(3) {
                             [0]=> int(0)
                             [1]=> int(0) 
                             [2]=> int(0) }
            ["size"]=> array(3) {
                             [0]=> int(11)
                             [1]=> int(13)
                             [2]=> int(13) } 
     }
  • 配列を巡回し、各ファイルを指定された場所
  • に書き込む必要があります.
    *私のphpは毛皮の中の毛皮しか点けませんので、上の内容ははっきりしないか正しくないかもしれません.*私のphpは毛皮の中の毛皮しか点けませんので、上の内容ははっきりしないか正しくないかもしれません.*私のphpは毛皮の中の毛皮しか点けませんので、上の内容ははっきりしないか正しくないかもしれません.*を指摘してください.*
    2.3プレゼンテーション結果
    ブログ見えない?私に図を見せて
    2.4 Androidでの実装-方法一(low)
    ```
    @Multipart
    @POST("/fileabout.php")
    Call upload_2(@Part("filedes") String des,@Part("file[]\"; filename=\"1.txt") RequestBody imgs,@Part("file[]\"; filename=\"2.txt") RequestBody imgs_2,@Part("file[]\"; filename=\"3.txt") RequestBody imgs_3);
  • apiインタフェースで書き込みが死滅し、柔軟性が悪く、実用価値がない
  • 注意file[]注意file[]注意file[]送信要求に関するコード
  • File file = new File(Environment.getExternalStorageDirectory() + "/" + "1.txt");
    File file2 = new File(Environment.getExternalStorageDirectory() + "/" + "2.txt");
    File file3 = new File(Environment.getExternalStorageDirectory() + "/" + "3.txt");
    final RequestBody requestBody =
                            RequestBody.create(MediaType.parse("multipart/form-data"),file);
    final RequestBody requestBody2 =
                            RequestBody.create(MediaType.parse("multipart/form-data"),file2);
    final RequestBody requestBody3 =
                            RequestBody.create(MediaType.parse("multipart/form-data"),file3);
    Call<String> model = service.upload_2("this is txt",requestBody,requestBody2,requestBody3);

    上記の方法は柔軟性のある科学研究がないので、使用価値がありません.では、私たちは次の方法を使う必要があります.
    2.5 Androidでの実現方法(二)
    対応するapiインタフェースがこのようになりました
    @Multipart
    @POST("/fileabout.php")
    Call upload_3(@Part("filedes") String des,@PartMap Map params);
  • これでpartを柔軟に構成できます
  • では、クライアントは次のような方法で構成することができます.
    Map<String,RequestBody> params = new HashMap<String, RequestBody>();
    params.put("file[]\"; filename=\""+file.getName()+"", requestBody);
    params.put("file[]\"; filename=\""+file2.getName()+"", requestBody2);
    params.put("file[]\"; filename=\""+file3.getName()+"", requestBody3);
    Call<String> model = service.upload_3("hello",params);

    柔軟性が向上したのではないでしょうか.これでformフォームのように、自由に構成できます.
    2.6結果表示
    ブログ見えない?私はここまで図を见て、私达のロットのアップロードは终わって、もし各位の友达は何かもっと良い方法があるならば、私に教えてもらいます...
    3.アップロード進捗の傍受
    この問題を考えたとき、全く考えがなかったので、気まずい思いをしました.よく考えてみると、やはり考えがありません.では、githubで公式に与えられたいくつかのクラスを見てみましょう.この類を見てこの類恩を見て、私は2枚の図をあげて、みんなは自分でブログを観察して見えませんか?ブログを見ても見えませんか?図を見て気づいたか?変換器にRequestBodyが現れて、これは私に瞬間的に考えさせて、間違いなく、私たちはダウンロードの方法を真似て、同じように、この種類を改造します.
    3.1 ChunkingConverterFactoryの改造
    まず、私たちは中のRequestBodyを捨てて、私たちは手動で中に伝えます.つまり、次のコードを削除します.
    final RequestBody realBody = delegate.convert(value)

    第2ステップでは,return new RequestBody()関連コードに長さ情報がないことを見出した.コードを追加します.
    @Override
    public long contentLength() throws IOException {
        return requestBody.contentLength();
    }

    第3部はダウンロードの過程をまねて、アップロードの過程を書いて、コードは以下の通りです
    @Override
    public void writeTo(BufferedSink sink) throws IOException{
    //                        realBody.writeTo(sink);
        if (bufferedSink == null) {
             //  
             bufferedSink = Okio.buffer(sink(sink));
         }
         //  
         requestBody.writeTo(bufferedSink);
         //    flush,                
         bufferedSink.flush();
    
    }
    
    private Sink sink(Sink sink) {
        return new ForwardingSink(sink) {
              //       
              long bytesWritten = 0L;
              //     ,      contentLength()  
              long contentLength = 0L;
    
              @Override
              public void write(Buffer source, long byteCount) throws IOException {
                   super.write(source, byteCount);
                   if (contentLength == 0) {
                        //  contentLength  ,      
                        contentLength = contentLength();
                    }
                    //          
                    bytesWritten += byteCount;
                    //                          
                    listener.onProgress(bytesWritten, contentLength, bytesWritten == contentLength);
                  }
             };
     }

    最後に、このクラスはこのようになりました.皆さんも私のgithubに行ってこのクラスをダウンロードすることができます.リンクアドレス
    public class ChunkingConverterFactory extends Converter.Factory {
    
        @Target(PARAMETER)
        @Retention(RUNTIME)
        @interface Chunked {
    
        }
    
        private BufferedSink bufferedSink;
        private final RequestBody requestBody;
    
        private final ProgressListener listener;
    
        public ChunkingConverterFactory(RequestBody requestBody,ProgressListener listener){
            this.requestBody = requestBody;
            this.listener = listener ;
        }
    
        @Override
        public Converter, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
    
            boolean isBody = false;
            boolean isChunked = false;
    
            for (Annotation annotation : parameterAnnotations){
                isBody |= annotation instanceof Body;
                isChunked |= annotation instanceof Chunked;
            }
    
            final Converter delegate = retrofit
                    .nextRequestBodyConverter(this,type,parameterAnnotations,methodAnnotations);
    
            return new Converter() {
                @Override
                public RequestBody convert(Object value) throws IOException {
    
    
                    return new RequestBody() {
                        @Override
                        public MediaType contentType() {
                            return requestBody.contentType();
                        }
    
    
                        @Override
                        public long contentLength() throws IOException {
                            return requestBody.contentLength();
                        }
    
                        @Override
                        public void writeTo(BufferedSink sink) throws IOException {
    //                        realBody.writeTo(sink);
                            if (bufferedSink == null) {
                                //  
                                bufferedSink = Okio.buffer(sink(sink));
                            }
                            //  
                            requestBody.writeTo(bufferedSink);
                            //    flush,                
                            bufferedSink.flush();
    
                        }
    
                        private Sink sink(Sink sink) {
                            return new ForwardingSink(sink) {
                                //       
                                long bytesWritten = 0L;
                                //     ,      contentLength()  
                                long contentLength = 0L;
    
                                @Override
                                public void write(Buffer source, long byteCount) throws IOException {
                                    super.write(source, byteCount);
                                    if (contentLength == 0) {
                                        //  contentLength  ,      
                                        contentLength = contentLength();
                                    }
                                    //          
                                    bytesWritten += byteCount;
                                    //  
                                    listener.onProgress(bytesWritten, contentLength, bytesWritten == contentLength);
                                }
                            };
                        }
                    };
                }
            };
        }
    
    
    }

    3.2アップロードの進行状況を監視する
    ダウンロードするように、私たちはbuilderでbuildオブジェクトに行きます.もちろん普通の方法も使えますが、RequestBodyを前に書かなければなりません.これは少し変に見えます.コード全体は次のとおりです.
    private void uploadProgress(){
            Retrofit.Builder builder = new Retrofit.Builder()
                    .baseUrl("http://192.168.56.1");
            File file = new File(Environment.getExternalStorageDirectory() + "/" + "text_img.png");
            final RequestBody requestBody =
                    RequestBody.create(MediaType.parse("multipart/form-data"),file);
            uploadfileApi api = builder.addConverterFactory(new ChunkingConverterFactory(requestBody, new ProgressListener() {
                @Override
                public void onProgress(long progress, long total, boolean done) {
                    Log.e(TAG, "onProgress:       " + progress + "total ---->"  + total );
                    Log.e(TAG, "onProgress: " + Looper.myLooper());
                }
            })).addConverterFactory(GsonConverterFactory.create()).build().create(uploadfileApi.class);
            Call model = api.upload("hh",requestBody);
            model.enqueue(new Callback() {
                @Override
                public void onResponse(Call call, Response response) {
    
                }
    
                @Override
                public void onFailure(Call call, Throwable t) {
    
                }
            });
        }

    3.3結果のプレゼンテーション
    ブログ見えない?私に図を見せて
    3.4一括アップロードの進捗監視
    私たちはどのように単一のファイルのアップロードの進度を傍受するかを知っていて、複数のファイル、うん、言わないでください、(複数の変換器を追加します).
    4.まとめ
    Retrofitはとても强大で、ある学友は私にRxjavaに协力して书きたいと思って、ああ、友达、メンツをあげて、どうにか私の第1篇の基础の使い方を见てみます.まだたくさんの机能が绍介されていないので、友达が何か必要なものがあるかを见て、私に伝言を残して、一绪に研究を终えましょう.