サーバーにストリーム全体を保存および編集せずに、クライアントに送信されるストリームから Base64 プレフィックスを削除するにはどうすればよいですか?
ストリームをクライアントに送信する際にプレフィックスを削除するのは、かなり複雑な作業です。サーバーに文字列全体を保存してもかまわない場合は、次のように簡単に実行できます。
BufferedReader br = null;
StringBuilder sb = new StringBuilder();
String line;
try {
br = new BufferedReader(new InputStreamReader(stream));
while ((line = br.readLine()) != null) {
sb.append(line);
}
String result = sb.toString();
//comma is the charater which seperates the prefix and the Base64 String
int i = result.indexOf(",");
result = result.substring(i + 1);
//Now, that we have just the Base64 encoded String, we can decode it
Base64.Decoder decoder = Base64.getDecoder();
byte[] decoded = decoder.decode(result);
//Now, just write each byte from the byte array to the output stream
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
しかし、ストリーム全体をサーバーに保存しないでより効率的にするには、はるかに複雑なタスクが作成されます。Base64.Decoder.wrap() メソッドを使用することもできますが、問題は、デコードできない値に達した場合に IOException をスローすることです (バイトをデコードできない場合はどうなりますか?)。残念ながら、Base64 でエンコードされていないため、Base64 プレフィックスはデコードできません。そのため、IOException がスローされます。
この問題を回避するには、 を使用して、指定された適切な でInputStreamReader
を読み取る必要があります。次に、InputStream の read() メソッド呼び出しから受け取った s を sにキャストする必要があります。適切な文字数に達したら、それを Base64 プレフィックスのイントロ (「データ」) と比較する必要があります。一致する場合、ストリームにプレフィックスが含まれていることがわかっているので、プレフィックスの終了文字 (コンマ: ",") に到達するまで読み続けます。最後に、プレフィックスの後のバイトのストリーミングを開始できます。例:InputStream
Charset
int
char
S3Object obj = s3Client.getObject(new GetObjectRequest(bucketName, keyName));
Base64.Decoder decoder = Base64.getDecoder();
InputStream stream = obj.getObjectContent();
InputStreamReader reader = new InputStreamReader(stream);
try{
return new StreamingOutput(){
@Override
public void write(OutputStream output) throws IOException, WebApplicationException{
//for checking if string has base64 prefix
char[] pre = new char[4]; //"data" has at most four bytes on a UTF-8 encoding
boolean containsPre = false;
int count = 0;
int nextByte = 0;
while((nextByte = stream.read()) != -1){
if(count < pre.length){
pre[count] = (char) nextByte;
count++;
}else if(count == pre.length){
//determine whether has prefix or not and act accordingly
count++;
containsPre = (Arrays.toString(pre).toLowerCase().equals("data")) ? true : false;
if(!containsPre){
//doesn't have Base64 prefix so write all the bytes until this point
for(int i = 0; i < pre.length; i++){
output.write((int) pre[i]);
}
output.write(nextByte);
}
}else if(containsPre && count < 25){
//the comma character (,) is considered the end of the Base64 prefix
//so look for the comma, but be realistic, if we don't find it at about 25 characters
//we can assume the String is not encoded correctly
containsPre = (Character.toString((char) nextByte).equals(",")) ? false : true;
count++;
}else{
output.write(nextByte);
}
}
output.flush();
output.close();
stream.close();
}
};
}catch(Exception e){
e.printStackTrace();
return null;
}
これはサーバー上で行うには少し重い作業のように思えるので、クライアント側でデコードする方が良い選択だと思います。残念ながら、ほとんどの Android クライアント側ライブラリは Base64 デコード (特にプレフィックス付き) をサポートしていません。ただし、@tyronen が指摘したように、文字列が既に取得されている場合、Fresco はそれをサポートします。ただし、これにより、画像読み込みライブラリを使用する主な理由の 1 つがなくなります。
Android クライアント側のデコード
クライアント側のアプリケーションでデコードするのはとても簡単です。最初に InputStream から文字列を取得します。
BufferedReader br = null;
StringBuilder sb = new StringBuilder();
String line;
try {
br = new BufferedReader(new InputStreamReader(stream));
while ((line = br.readLine()) != null) {
sb.append(line);
}
return sb.toString();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
次に、Android の Base64 クラスを使用して String をデコードします。
int i = result.indexOf(",");
result = result.substring(i + 1);
byte[] decodedString = Base64.decode(result, Base64.DEFAULT);
Bitmap bitMap = BitmapFactory.decodeByteArray(decodedString, 0, decodedString.length);
Fresco ライブラリは、多くの委譲を使用しているため、更新が難しいようです。そこで、Picasso 画像読み込みライブラリの使用に移り、Base64 デコード機能を備えた独自のフォークを作成しました。