Skip to content

Commit 59bf1a9

Browse files
committed
Add prototype implementation of bandwidth limit and test code
Signed-off-by: Harvey Lelliott <42912136+flip-dots@users.noreply.github.com>
1 parent 17a65c2 commit 59bf1a9

File tree

5 files changed

+406
-30
lines changed

5 files changed

+406
-30
lines changed

library/src/main/java/com/owncloud/android/lib/common/network/FileRequestEntity.java

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,17 @@
1616
import java.io.FileNotFoundException;
1717
import java.io.IOException;
1818
import java.io.OutputStream;
19-
import java.io.RandomAccessFile;
20-
import java.nio.ByteBuffer;
21-
import java.nio.channels.FileChannel;
2219
import java.util.Collection;
2320
import java.util.HashSet;
2421
import java.util.Iterator;
2522
import java.util.Set;
2623

24+
import okio.Throttler;
25+
import okio.Source;
26+
import okio.Sink;
27+
import okio.BufferedSink;
28+
import okio.Okio;
29+
2730
/**
2831
* A RequestEntity that represents a File.
2932
*/
@@ -32,6 +35,7 @@ public class FileRequestEntity implements RequestEntity, ProgressiveDataTransfer
3235
private final File file;
3336
private final String contentType;
3437
private final Set<OnDatatransferProgressListener> dataTransferListeners = new HashSet<>();
38+
private final Throttler throttler = new Throttler();
3539

3640
public FileRequestEntity(final File file, final String contentType) {
3741
super();
@@ -77,28 +81,45 @@ public void removeDataTransferProgressListener(OnDatatransferProgressListener li
7781
dataTransferListeners.remove(listener);
7882
}
7983
}
80-
81-
84+
85+
/**
86+
* @param limit Maximum upload speed in bytes per second.
87+
* Disabled by default (limit 0).
88+
*/
89+
public void setBandwidthLimit(long limit) {
90+
throttler.bytesPerSecond(limit);
91+
}
92+
8293
@Override
8394
public void writeRequest(final OutputStream out) throws IOException {
84-
ByteBuffer tmp = ByteBuffer.allocate(4096);
85-
int readResult;
86-
87-
RandomAccessFile raf = new RandomAccessFile(file, "r");
88-
FileChannel channel = raf.getChannel();
95+
long readResult;
8996
Iterator<OnDatatransferProgressListener> it;
9097
long transferred = 0;
9198
long size = file.length();
9299
if (size == 0) size = -1;
100+
101+
Source source = null;
102+
Source bufferSource = null;
103+
Sink sink = null;
104+
Sink throttledSink = null;
105+
BufferedSink bufferedThrottledSink = null;
93106
try {
94-
while ((readResult = channel.read(tmp)) >= 0) {
107+
source = Okio.source(file);
108+
bufferSource = Okio.buffer(source);
109+
110+
sink = Okio.sink(out);
111+
throttledSink = throttler.sink(sink);
112+
bufferedThrottledSink = Okio.buffer(throttledSink);
113+
114+
while ((readResult = bufferSource.read(bufferedThrottledSink.getBuffer(), 4096)) >= 0) {
95115
try {
96-
out.write(tmp.array(), 0, readResult);
116+
bufferedThrottledSink.emitCompleteSegments();
117+
97118
} catch (IOException io) {
98119
// work-around try catch to filter exception in writing
99120
throw new WriteException(io);
100121
}
101-
tmp.clear();
122+
102123
transferred += readResult;
103124
synchronized (dataTransferListeners) {
104125
it = dataTransferListeners.iterator();
@@ -107,6 +128,7 @@ public void writeRequest(final OutputStream out) throws IOException {
107128
}
108129
}
109130
}
131+
bufferedThrottledSink.flush();
110132

111133
} catch (IOException io) {
112134
// any read problem will be handled as if the file is not there
@@ -123,8 +145,12 @@ public void writeRequest(final OutputStream out) throws IOException {
123145

124146
} finally {
125147
try {
126-
channel.close();
127-
raf.close();
148+
// TODO Which of these are even necessary? (Been a while since I last dealt with buffers)
149+
if (source != null) source.close();
150+
if (bufferSource != null) bufferSource.close();
151+
// if (sink != null) sink.close();
152+
// if (throttledSink != null) throttledSink.close();
153+
// if (bufferedThrottledSink != null) bufferedThrottledSink.close();
128154
} catch (IOException io) {
129155
// ignore failures closing source file
130156
}

library/src/main/java/com/owncloud/android/lib/resources/files/DownloadFileRemoteOperation.java

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,12 @@
1818
import org.apache.commons.httpclient.HttpStatus;
1919
import org.apache.commons.httpclient.methods.GetMethod;
2020

21-
import java.io.BufferedInputStream;
21+
import okio.Throttler;
22+
import okio.Source;
23+
import okio.BufferedSink;
24+
import okio.Okio;
25+
2226
import java.io.File;
23-
import java.io.FileOutputStream;
2427
import java.io.IOException;
2528
import java.util.Date;
2629
import java.util.HashSet;
@@ -44,6 +47,7 @@ public class DownloadFileRemoteOperation extends RemoteOperation {
4447
private long modificationTimestamp = 0;
4548
private String eTag = "";
4649
private GetMethod getMethod;
50+
private final Throttler throttler = new Throttler();
4751

4852
private String remotePath;
4953
private String temporalFolderPath;
@@ -57,8 +61,16 @@ public DownloadFileRemoteOperation(String remotePath, String temporalFolderPath)
5761
this.temporalFolderPath = temporalFolderPath;
5862
}
5963

60-
@Override
61-
protected RemoteOperationResult run(OwnCloudClient client) {
64+
/**
65+
* @param limit Maximum download speed in bytes per second.
66+
* Disabled by default (limit 0).
67+
*/
68+
public void setBandwidthLimit(long limit) {
69+
throttler.bytesPerSecond(limit);
70+
}
71+
72+
@Override
73+
protected RemoteOperationResult run(OwnCloudClient client) {
6274
RemoteOperationResult result;
6375

6476
/// download will be performed to a temporal file, then moved to the final location
@@ -88,7 +100,11 @@ private int downloadFile(OwnCloudClient client, File targetFile) throws IOExcept
88100
getMethod = new GetMethod(client.getFilesDavUri(remotePath));
89101
Iterator<OnDatatransferProgressListener> it;
90102

91-
FileOutputStream fos = null;
103+
// TODO If the upload and download limits should be global then the same throttler can be used for
104+
// all instances of this and the upload class.
105+
Source bufferSource = null;
106+
Source throttledBufferSource = null;
107+
BufferedSink bufferSink = null;
92108
try {
93109
status = client.executeMethod(getMethod);
94110
if (isSuccess(status)) {
@@ -98,25 +114,26 @@ private int downloadFile(OwnCloudClient client, File targetFile) throws IOExcept
98114
Log_OC.e(TAG, "Error creating file " + targetFile.getAbsolutePath(), ex);
99115
throw new CreateLocalFileException(targetFile.getPath(), ex);
100116
}
101-
BufferedInputStream bis = new BufferedInputStream(getMethod.getResponseBodyAsStream());
102-
fos = new FileOutputStream(targetFile);
117+
bufferSource = Okio.source(getMethod.getResponseBodyAsStream());
118+
throttledBufferSource = throttler.source(bufferSource);
119+
bufferSink = Okio.buffer(Okio.sink(targetFile));
120+
103121
long transferred = 0;
104122

105123
Header contentLength = getMethod.getResponseHeader("Content-Length");
106124
long totalToTransfer = (contentLength != null &&
107-
contentLength.getValue().length() > 0) ?
108-
Long.parseLong(contentLength.getValue()) : 0;
125+
contentLength.getValue().length() > 0) ?
126+
Long.parseLong(contentLength.getValue()) : 0;
109127

110-
byte[] bytes = new byte[4096];
111-
int readResult;
112-
while ((readResult = bis.read(bytes)) != -1) {
128+
long readResult;
129+
while ((readResult = throttledBufferSource.read(bufferSink.getBuffer(), 4096)) != -1) {
130+
bufferSink.emitCompleteSegments();
113131
synchronized (mCancellationRequested) {
114132
if (mCancellationRequested.get()) {
115133
getMethod.abort();
116134
throw new OperationCancelledException();
117135
}
118136
}
119-
fos.write(bytes, 0, readResult);
120137
transferred += readResult;
121138
synchronized (mDataTransferListeners) {
122139
it = mDataTransferListeners.iterator();
@@ -126,6 +143,7 @@ private int downloadFile(OwnCloudClient client, File targetFile) throws IOExcept
126143
}
127144
}
128145
}
146+
bufferSink.flush();
129147
// Check if the file is completed
130148
// if transfer-encoding: chunked we cannot check if the file is complete
131149
Header transferEncodingHeader = getMethod.getResponseHeader("Transfer-Encoding");
@@ -163,7 +181,11 @@ private int downloadFile(OwnCloudClient client, File targetFile) throws IOExcept
163181
}
164182

165183
} finally {
166-
if (fos != null) fos.close();
184+
// TODO Any of these need try statements? Which of these are even necessary? (Been a while since I last dealt with buffers)
185+
if (bufferSource != null) bufferSource.close();
186+
if (throttledBufferSource != null) throttledBufferSource.close();
187+
if (bufferSink != null) bufferSink.close();
188+
167189
if (!savedFile && targetFile.exists()) {
168190
targetFile.delete();
169191
}

library/src/main/java/com/owncloud/android/lib/resources/files/UploadFileRemoteOperation.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ public class UploadFileRemoteOperation extends RemoteOperation<String> {
6161
final Set<OnDatatransferProgressListener> dataTransferListeners = new HashSet<>();
6262

6363
protected RequestEntity entity = null;
64+
private long bandwidthLimit = 0;
6465

6566
@VisibleForTesting
6667
public UploadFileRemoteOperation() {
@@ -134,6 +135,20 @@ public UploadFileRemoteOperation(String localPath,
134135
this.creationTimestamp = creationTimestamp;
135136
}
136137

138+
/**
139+
* @param limit Maximum upload speed in bytes per second.
140+
* Disabled by default (limit 0).
141+
*/
142+
public void setBandwidthLimit(long limit) {
143+
bandwidthLimit = limit;
144+
145+
// If already in progress then set the limit immediately
146+
// Otherwise it will be saved and set when it's run.
147+
if (entity != null) {
148+
((FileRequestEntity) entity).setBandwidthLimit(limit);
149+
}
150+
}
151+
137152
@Override
138153
protected RemoteOperationResult<String> run(OwnCloudClient client) {
139154
RemoteOperationResult<String> result;
@@ -193,6 +208,7 @@ protected RemoteOperationResult<String> uploadFile(OwnCloudClient client) throws
193208
try {
194209
File f = new File(localPath);
195210
entity = new FileRequestEntity(f, mimeType);
211+
((FileRequestEntity) entity).setBandwidthLimit(bandwidthLimit);
196212
synchronized (dataTransferListeners) {
197213
((ProgressiveDataTransfer) entity)
198214
.addDataTransferProgressListeners(dataTransferListeners);

0 commit comments

Comments
 (0)