AndroidでのカスタムMultipartEntityによるファイルアップロードおよびVolleyライブラリによるファイルアップロード


カスタム実装MultipartEntity
ネットワークプロトコルを使用してデータを転送することは、あるプロトコルに従うことにほかならないことを知っています.私たちはモバイルアプリケーションを開発する際に基本的にHTTPプロトコルを使用しています.HTTPプロトコルは、TCPベースのネットワーク要求プロトコルであり、そのプロトコルに規定されたフォーマットに基づいてデータを転送し、サーバがデータを返します.プロトコルパラメータが間違って渡された場合、サーバはエラーを返すしかありません.
これはスパイの間で暗号に少し似ていて、彼らは規定の暗号を持っていて、双方が会って、Aは言います: 天王蓋地虎、B対:宝塔鎮河妖.そうだ、話をする.すみません、このBを殺します.HTTPもそうですが、HTTPリクエスト時にヘッダとパラメータを追加し、サーバがパラメータに基づいて解析します.次のようになります. 
POST /api/feed/ HTTP/1.1
   header  
  2

フォーマットに基づいてサーバにリクエストを送信すれば万事順調です!次に、MultipartEntityの実装について説明します.
public class MultipartEntity implements HttpEntity{private final static char[]MULTIPART_CHARS="-_123457890abcd efghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();/*******/private finalString NEW_LINE_STR="r";private finalString CONT_TYPE="Content-Type:";privavate finalString;private finalString CONT_TYPE="te final String CONTENT_DISPOSITION=「Content-Disposition:」;/**テキストパラメータと文字セット*/private final String TYPE_TEXT_CHARSET=「text/plain;charset=UTF-8」;/**バイトフローパラメータ*/private final String TYPE_OCTET_STREAM=「application/octet-stream」;/***バイナリパラメータ*/private final byte[]BINARY_ENCODING=「Content-transfer-Encoding:binaryrr」.getBytes();/***テキストパラメータ*/private finalbyte[]BIT_ENCODING="Content-transfer-Encoding:8 bitrr".getBytes();/***セパレータ*/private String mBoundary=null;/**出力ストリーム*/ByteArrayOutputStreammOutputStream=new ByteArrayOutputStream();public MultipartEntity(){this.mBoundary=genateBoundary();}//***生成区切り記号**@return*/private final String generateBoundary(){final StringBuffer buf=new StringBuffer();final Random rand=new Random();for(int i=0;i<30;i+){buf.append(MULTIPART_CHARS[rand.nextInt(MULTIPART_CHARS.length)]);}return buf.toString();}*パラメータの先頭の区切り文字**@throws IOException*/private void writeFirstBoundary()throws IOException{mOutputStream.write(("--"+mBoundary+"r").getBytes();//****テキストパラメータ*@param key*@param value*/public void addStringPart( finalString paramName,finalString value){writeToututututputStrtStrStrStringPart**@param value***@param value**/public void addStringPart( finalString paramName,finalString value){writeTouteOutputStruteam(paramName,value.getBytes()、TYPE_TEXT_CHARSET,BIT_ENCODING,"");}/*******@param key*@param rawData*@param type*@param encodingBytes*@param file Name*/private void writeToOutputStream(String paramName,byte[]rawData,String type,byte[]encodingBytee[]encodingBytes,String fileName Name,String fileNamName,byte[]e){try{writeFirstBoundary(); mOutputStream.write((CONTENT_TYPE + type + NEW_LINE_STR).getBytes()); mOutputStream .write(getContentDispositionBytes(paramName, fileName)); mOutputStream.write(encodingBytes); mOutputStream.write(rawData); mOutputStream.write(NEW_LINE_STR.getBytes()); } catch (final IOException e) { e.printStackTrace(); } }/***Bitmapのバイトフローパラメータ*@param key*@param rawData*/public void addBinaryPart(String paramName,final byte[]rawData){writeTooutputStream(paramName,rawData,TYPE_OCTET_STREAM,BINARY_ENCODING,"no-file");//***ファイルパラメータを追加することで、ファイルアップロード機能を実現することができます*@param key*@param file*/public void addFilePart(final String key,final File){InputStream fin=null;try{fin=new FileInputStream(file);writeFirstBoundary();final String type=CONTT_TYPE_TEOCT_STREAM+NEW_LINE_STR;mutputStream.write(getContentDispositionBytes(key, file.getName())); mOutputStream.write(type.getBytes()); mOutputStream.write(BINARY_ENCODING); final byte[] tmp = new byte[4096]; int len = 0; while ((len = fin.read(tmp)) != -1) { mOutputStream.write(tmp, 0, len); } mOutputStream.flush(); } catch (final IOException e) { e.printStackTrace(); } finally { closeSilently(fin); } } private void closeSilently(Closeable closeable) { try { if (closeable != null) { closeable.close(); } } catch (final IOException e) { e.printStackTrace(); } } private byte[] getContentDispositionBytes(String paramName, String fileName) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append(CONTENT_DISPOSITION+「form-data;name=」+「+paramName+」");////////テキストパラメータfilenameパラメータがなく、空に設定すればif(!TextUtils.isEmpty(fileName){stringBuilder.append(";filename="+""""+fileName+""""");}returnstringBuilder.append(NEW_LINE_STR).toString().getBytes();}@Override public loblic loblic loloc;;}@Override ng getContentLength(){ return mOutputStream.toByteArray().length; } @Override public Header getContentType() { return new BasicHeader("Content-Type", "multipart/form-data; boundary="+ mBoundary); } @Override public boolean isChunked() { return false; } @Override public boolean isRepeatable() { return false; } @Override public boolean isStreaming() { return false; }@Override public void writeTo(finalOutputStream outstream)throws IOException{////パラメータの最後尾の終端子finalString endString=「--」+mBoundary+「--r」////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////rnnull;} @Override public void consumeContent() throws IOException, UnsupportedOperationException { if (isStreaming()) { throw new UnsupportedOperationException( "Streaming entity does not implement #consumeContent()"); } } @Override public InputStream getContent() { return new ByteArrayInputStream(mOutputStream.toByteArray()); }}
ユーザーは、addStringPart、addBinaryPart、addFilePartでパラメータを追加できます.それぞれ文字列パラメータの追加、バイナリパラメータの追加、ファイルパラメータの追加を表します.MultipartEntityにはByteArrayOutputStreamオブジェクトがあり、これらのパラメータをこの出力ストリームに書き込み、ネットワークリクエストを実行するとwriteTo(final OutputStream outstream)が実行されます.
メソッドは、すべてのパラメータのバイトストリームデータをサーバに対して確立されたTCP接続の出力ストリームに書き込むことにより、私たちのパラメータをサーバに渡します.もちろん、その前に、ByteArrayOutputStreamオブジェクトにフォーマットに従ってデータを書く必要があります.たとえば、私はサーバにテキスト、bitmapピクチャ、ファイルを送信します.つまり、この要求には3つのパラメータがあります.数.コードは次のとおりです. 
MultiparartEntity=multipartEntity=new MultiparartEntity();/////テキストパラメータmultipartEntity.addStringPart("type","私のテキストパラメータ");Bitmap bmp=BitmaapFactory.decodeResource( getResources(),R.drawable.thumb);//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////FilePart(「images」「,new File("storage/emulated/0/test.jpg");//POST要求HttpPost=new HttpPost("url");//multipartEntityをpost post.setEntity(multipartEntity);//http clientを使用して要求HttpClient httpClient=new DefaultHttpClient();httpClient.execute(post);
MultipartEntityの出力フォーマットは次のようになります. 
POST/api/feed/HTTP/1.1Content-Type: multipart/form-data; boundary=o3Fhj53z-oKToduAElfBaNU4pZhp4-User-Agent: Dalvik/1.6.0 (Linux; U; Android 4.4.4; M040 Build/KTU84P)Host: www.myhost.comConnection: Keep-AliveAccept-Encoding: gzipContent-Length: 168518--o3Fhj53z-oKToduAElfBaNU4pZhp4-Content-Type: text/plain; charset=UTF-8Content-Disposition: form-data; name="type"Content-Transfer-Encoding: 8bitThis my type--o3Fhj53z-oKToduAElfBaNU4pZhp4-Content-Type: application/octet-streamContent-Disposition: form-data; name=「images」;filename=「no-file」Content-transfer-Encoding:binaryここではbitmapのバイナリデータであるo 3 Fhj 53 z-oKToduAElfBaNU 4 pZhp 4-Content-Type:application/octet-streamContent-Disposition:form-data;name=「file」;filename=「storage/emulated/0/test.jpg「Content-transfer-Encoding:binaryここではピクチャファイルのバイナリデータ--o 3 Fhj 53 z-oKToduAElfBaNU 4 pZhp 4---
よく知っていますね.これが冒頭で述べたPOSTメッセージフォーマットです.間違いありません.HttpEntityは、パラメータをHTTPのメッセージフォーマットに構築し、テキストパラメータがどのフォーマットで、ファイルがどのフォーマットで、どのタイプで、これらのフォーマットが固定されています.構築が完了すると、リクエストの実行時にhttpリクエストの出力ストリームがwriteTo(OutputStream)を通ります.関数を渡し、これらのパラメータデータをhttp出力ストリームにすべて出力すればよい.これらの理屈が分かれば、コードを見ればわかるだろう.
Volleyでのファイルアップロードの実装
VolleyはGoogleが公式に発表したネットリクエストライブラリです.このライブラリは簡素で優秀ですが、ファイルアップロード機能をデフォルトで追加するサポートもありません.今日はRequestをカスタマイズしてファイルアップロード機能を実現するか、上のMultipartEntityクラスを借りる必要があります.コードを見てみましょう.
/**
 * @author mrsimple
 */
public class MultipartRequest extends Request {

    MultipartEntity mMultiPartEntity = new MultipartEntity();

    Map mHeaders = new HashMap();

    private final Listener mListener;

    /**
     * Creates a new request with the given url.
     *
     * @param url URL to fetch the string at
     * @param listener Listener to receive the String response
     */
    public MultipartRequest(String url, Listener listener) {
        this(url, listener, null);
    }

    /**
     * Creates a new POST request.
     *
     * @param url URL to fetch the string at
     * @param listener Listener to receive the String response
     * @param errorListener Error listener, or null to ignore errors
     */
    public MultipartRequest(String url, Listener listener, ErrorListener errorListener) {
        super(Method.POST, url, errorListener);
        mListener = listener;
    }

    /**
     * @return
     */
    public MultipartEntity getMultiPartEntity() {
        return mMultiPartEntity;
    }

    @Override
    public String getBodyContentType() {
        return mMultiPartEntity.getContentType().getValue();
    }

    public void addHeader(String key, String value) {
        mHeaders.put(key, value);
    }

    @Override
    public Map getHeaders() throws AuthFailureError {
        return mHeaders;
    }

    @Override
    public byte[] getBody() {

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            // multipart body
            mMultiPartEntity.writeTo(bos);
        } catch (IOException e) {
            Log.e("", "IOException writing to ByteArrayOutputStream");
        }
        return bos.toByteArray();
    }

    @Override
    protected Response parseNetworkResponse(NetworkResponse response) {
        String parsed = "";
        try {
            parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
        } catch (UnsupportedEncodingException e) {
            parsed = new String(response.data);
        }
        return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
    }

    @Override
    protected void deliverResponse(String response) {
        if (mListener != null) {
            mListener.onResponse(response);
        }
    }

}

サンプルコードを使用するには、次の手順に従います. 
        RequestQueue queue = Volley.newRequestQueue(this);

        MultipartRequest multipartRequest = new MultipartRequest(
                "http://yourhost.com", new Listener() {

                    @Override
                    public void onResponse(String response) {
                        Log.e("", "### response : " + response);
                    }

                });

        //   header
        multipartRequest.addHeader("header-name", "value");

        //   MultipartEntity     
        MultipartEntity multi = multipartRequest.getMultiPartEntity();
        //     
        multi.addStringPart("location", "       ");
        multi.addStringPart("type", "0");

        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.thumb);
        //      Bitmap
        multi.addBinaryPart("images", bitmapToBytes(bitmap));
        //     
        multi.addFilePart("imgfile", new File("storage/emulated/0/test.jpg"));
        //          
        queue.add(multipartRequest);