J'essaie d'implémenter une barre de progression pour indiquer la progression d'un téléchargement de fichier en plusieurs parties.
J'ai lu à partir d'un commentaire sur cette réponse - https://stackoverflow.com/a/24285633/1022454 que je dois encapsuler le récepteur transmis au RequestBody et fournir un rappel qui suit les octets déplacés .
J'ai créé un RequestBody personnalisé et enveloppé le récepteur avec une classe CustomSink, mais grâce au débogage, je peux voir que les octets sont écrits par RealBufferedSink ln 44 et la méthode d'écriture du récepteur personnalisé n'est exécutée qu'une seule fois, non me permettant de suivre les octets déplacés.
private class CustomRequestBody extends RequestBody {
MediaType contentType;
byte[] content;
private CustomRequestBody(final MediaType contentType, final byte[] content) {
this.contentType = contentType;
this.content = content;
}
@Override
public MediaType contentType() {
return contentType;
}
@Override
public long contentLength() {
return content.length;
}
@Override
public void writeTo(BufferedSink sink) throws IOException {
CustomSink customSink = new CustomSink(sink);
customSink.write(content);
}
}
private class CustomSink implements BufferedSink {
private static final String TAG = "CUSTOM_SINK";
BufferedSink bufferedSink;
private CustomSink(BufferedSink bufferedSink) {
this.bufferedSink = bufferedSink;
}
@Override
public void write(Buffer source, long byteCount) throws IOException {
Log.d(TAG, "source size: " + source.size() + " bytecount" + byteCount);
bufferedSink.write(source, byteCount);
}
@Override
public void flush() throws IOException {
bufferedSink.flush();
}
@Override
public Timeout timeout() {
return bufferedSink.timeout();
}
@Override
public void close() throws IOException {
bufferedSink.close();
}
@Override
public Buffer buffer() {
return bufferedSink.buffer();
}
@Override
public BufferedSink write(ByteString byteString) throws IOException {
return bufferedSink.write(byteString);
}
@Override
public BufferedSink write(byte[] source) throws IOException {
return bufferedSink.write(source);
}
@Override
public BufferedSink write(byte[] source, int offset, int byteCount) throws IOException {
return bufferedSink.write(source, offset, byteCount);
}
@Override
public long writeAll(Source source) throws IOException {
return bufferedSink.writeAll(source);
}
@Override
public BufferedSink writeUtf8(String string) throws IOException {
return bufferedSink.writeUtf8(string);
}
@Override
public BufferedSink writeString(String string, Charset charset) throws IOException {
return bufferedSink.writeString(string, charset);
}
@Override
public BufferedSink writeByte(int b) throws IOException {
return bufferedSink.writeByte(b);
}
@Override
public BufferedSink writeShort(int s) throws IOException {
return bufferedSink.writeShort(s);
}
@Override
public BufferedSink writeShortLe(int s) throws IOException {
return bufferedSink.writeShortLe(s);
}
@Override
public BufferedSink writeInt(int i) throws IOException {
return bufferedSink.writeInt(i);
}
@Override
public BufferedSink writeIntLe(int i) throws IOException {
return bufferedSink.writeIntLe(i);
}
@Override
public BufferedSink writeLong(long v) throws IOException {
return bufferedSink.writeLong(v);
}
@Override
public BufferedSink writeLongLe(long v) throws IOException {
return bufferedSink.writeLongLe(v);
}
@Override
public BufferedSink emitCompleteSegments() throws IOException {
return bufferedSink.emitCompleteSegments();
}
@Override
public OutputStream outputStream() {
return bufferedSink.outputStream();
}
}
Quelqu'un at-il un exemple de la façon dont je procéderais?
Vous devez créer un RequestBody personnalisé et remplacer la méthode writeTo, et là, vous devez envoyer vos fichiers dans le puits par segments. Il est très important de vider le récepteur après chaque segment, sinon votre barre de progression se remplira rapidement sans que le fichier ne soit réellement envoyé sur le réseau, car le contenu restera dans le récepteur (qui agit comme un tampon).
public class CountingFileRequestBody extends RequestBody {
private static final int SEGMENT_SIZE = 2048; // okio.Segment.SIZE
private final File file;
private final ProgressListener listener;
private final String contentType;
public CountingFileRequestBody(File file, String contentType, ProgressListener listener) {
this.file = file;
this.contentType = contentType;
this.listener = listener;
}
@Override
public long contentLength() {
return file.length();
}
@Override
public MediaType contentType() {
return MediaType.parse(contentType);
}
@Override
public void writeTo(BufferedSink sink) throws IOException {
Source source = null;
try {
source = Okio.source(file);
long total = 0;
long read;
while ((read = source.read(sink.buffer(), SEGMENT_SIZE)) != -1) {
total += read;
sink.flush();
this.listener.transferred(total);
}
} finally {
Util.closeQuietly(source);
}
}
public interface ProgressListener {
void transferred(long num);
}
}
Vous pouvez trouver une implémentation complète qui prend en charge l'affichage de la progression dans un AdapterView et également l'annulation des téléchargements sur mon Gist: https://Gist.github.com/eduardb/dd2dc530afd37108e1ac
RequestBody
personnalisé, pas besoin d'implémenter un BufferedSink
personnalisé. Nous pouvons allouer le tampon Okio pour lire à partir du fichier image, et connecter ce tampon au puits.Pour un exemple, veuillez consulter la fonction createCustomRequestBody
ci-dessous
public static RequestBody createCustomRequestBody(final MediaType contentType, final File file) {
return new RequestBody() {
@Override public MediaType contentType() {
return contentType;
}
@Override public long contentLength() {
return file.length();
}
@Override public void writeTo(BufferedSink sink) throws IOException {
Source source = null;
try {
source = Okio.source(file);
//sink.writeAll(source);
Buffer buf = new Buffer();
Long remaining = contentLength();
for (long readCount; (readCount = source.read(buf, 2048)) != -1; ) {
sink.write(buf, readCount);
Log.d(TAG, "source size: " + contentLength() + " remaining bytes: " + (remaining -= readCount));
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
}
utiliser -
.addPart(
Headers.of("Content-Disposition", "form-data; name=\"image\""),
createCustomRequestBody(MediaType.parse("image/png"), new File("test.jpg")))
.build()
Cette chose fonctionne très bien!
Gradle
dependencies {
compile 'io.github.lizhangqu:coreprogress:1.0.2'
}
//wrap your original request body with progress
RequestBody requestBody = ProgressHelper.withProgress(body, new ProgressUIListener()....}
Exemple de code complet ici https://github.com/lizhangqu/CoreProgress