Skip to content

Commit 9d42018

Browse files
committed
add range parameter in DownloadFileRequest
1 parent 78f2b43 commit 9d42018

File tree

4 files changed

+326
-10
lines changed

4 files changed

+326
-10
lines changed

src/main/java/com/aliyun/oss/internal/OSSDownloadOperation.java

Lines changed: 63 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ public int hashCode() {
221221
result = prime * result + (int) (end ^ (end >>> 32));
222222
result = prime * result + (int) (start ^ (start >>> 32));
223223
result = prime * result + (int) (crc ^ (crc >>> 32));
224+
result = prime * result + (int) (fileStart ^ (fileStart >>> 32));
224225
return result;
225226
}
226227

@@ -230,6 +231,7 @@ public int hashCode() {
230231
public boolean isCompleted; // flag of part download finished or not;
231232
public long length; // length of part
232233
public long crc; // part crc.
234+
public long fileStart; // start index in file, for range get
233235
}
234236

235237
static class PartResult {
@@ -412,7 +414,8 @@ private DownloadFileResult downloadFileWithCheckpoint(DownloadFileRequest downlo
412414
}
413415

414416
// check crc64
415-
if(objectOperation.getInnerClient().getClientConfiguration().isCrcCheckEnabled()) {
417+
if(objectOperation.getInnerClient().getClientConfiguration().isCrcCheckEnabled() &&
418+
!hasRangeInRequest(downloadFileRequest)) {
416419
Long clientCRC = calcObjectCRCFromParts(downloadResult.getPartResults());
417420
try {
418421
OSSUtils.checkChecksum(clientCRC, serverCRC, downloadResult.getObjectMetadata().getRequestId());
@@ -446,10 +449,17 @@ private void prepare(DownloadCheckPoint downloadCheckPoint, DownloadFileRequest
446449
downloadCheckPoint.bucketName = downloadFileRequest.getBucketName();
447450
downloadCheckPoint.objectKey = downloadFileRequest.getKey();
448451
downloadCheckPoint.objectStat = ObjectStat.getFileStat(objectOperation, downloadFileRequest);
449-
downloadCheckPoint.downloadParts = splitFile(downloadCheckPoint.objectStat.size,
450-
downloadFileRequest.getPartSize());
451-
452-
createFixedFile(downloadFileRequest.getTempDownloadFile(), downloadCheckPoint.objectStat.size);
452+
long downloadSize;
453+
if (downloadCheckPoint.objectStat.size > 0) {
454+
long[] slice = getSlice(downloadFileRequest.getRange(), downloadCheckPoint.objectStat.size);
455+
downloadCheckPoint.downloadParts = splitFile(slice[0], slice[1], downloadFileRequest.getPartSize());
456+
downloadSize = slice[1];
457+
} else {
458+
//download whole file
459+
downloadSize = 0;
460+
downloadCheckPoint.downloadParts = splitOneFile();
461+
}
462+
createFixedFile(downloadFileRequest.getTempDownloadFile(), downloadSize);
453463
}
454464

455465
public static void createFixedFile(String filePath, long length) throws IOException {
@@ -544,6 +554,10 @@ public int compare(PartResult p1, PartResult p2) {
544554
return downloadResult;
545555
}
546556

557+
private boolean hasRangeInRequest(DownloadFileRequest downloadFileRequest) {
558+
return downloadFileRequest.getRange() != null;
559+
}
560+
547561
static class Task implements Callable<PartResult> {
548562

549563
public Task(int id, String name, DownloadCheckPoint downloadCheckPoint, int partIndex,
@@ -569,7 +583,7 @@ public PartResult call() throws Exception {
569583
tr = new PartResult(partIndex + 1, downloadPart.start, downloadPart.end);
570584

571585
output = new RandomAccessFile(downloadFileRequest.getTempDownloadFile(), "rw");
572-
output.seek(downloadPart.start);
586+
output.seek(downloadPart.fileStart);
573587

574588
GetObjectRequest getObjectRequest = new GetObjectRequest(downloadFileRequest.getBucketName(),
575589
downloadFileRequest.getKey());
@@ -650,7 +664,7 @@ public ObjectMetadata GetobjectMetadata() {
650664
private ProgressListener progressListener;
651665
}
652666

653-
private ArrayList<DownloadPart> splitFile(long objectSize, long partSize) {
667+
private ArrayList<DownloadPart> splitFile(long start, long objectSize, long partSize) {
654668
ArrayList<DownloadPart> parts = new ArrayList<DownloadPart>();
655669

656670
long partNum = objectSize / partSize;
@@ -662,8 +676,9 @@ private ArrayList<DownloadPart> splitFile(long objectSize, long partSize) {
662676
for (int i = 0; offset < objectSize; offset += partSize, i++) {
663677
DownloadPart part = new DownloadPart();
664678
part.index = i;
665-
part.start = offset;
666-
part.end = getPartEnd(offset, objectSize, partSize);
679+
part.start = offset + start;
680+
part.end = getPartEnd(offset, objectSize, partSize) + start;
681+
part.fileStart = offset;
667682
parts.add(part);
668683
}
669684

@@ -677,6 +692,45 @@ private long getPartEnd(long begin, long total, long per) {
677692
return begin + per - 1;
678693
}
679694

695+
private ArrayList<DownloadPart> splitOneFile() {
696+
ArrayList<DownloadPart> parts = new ArrayList<DownloadPart>();
697+
DownloadPart part = new DownloadPart();
698+
part.index = 0;
699+
part.start = 0;
700+
part.end = -1;
701+
part.fileStart = 0;
702+
parts.add(part);
703+
return parts;
704+
}
705+
706+
private long[] getSlice(long[] range, long totalSize) {
707+
long start = 0;
708+
long size = totalSize;
709+
710+
if ((range == null) ||
711+
(range.length != 2) ||
712+
(totalSize < 1) ||
713+
(range[0] < 0 && range[1] < 0) ||
714+
(range[0] > 0 && range[1] > 0 && range[0] > range[1])||
715+
(range[0] >= totalSize)) {
716+
//download all
717+
} else {
718+
//dwonload part by range & total size
719+
long begin = range[0];
720+
long end = range[1];
721+
if (range[0] < 0) {
722+
begin = 0;
723+
}
724+
if (range[1] < 0 || range[1] >= totalSize) {
725+
end = totalSize -1;
726+
}
727+
start = begin;
728+
size = end - begin + 1;
729+
}
730+
731+
return new long[]{start, size};
732+
}
733+
680734
private boolean remove(String filePath) {
681735
boolean flag = false;
682736
File file = new File(filePath);

src/main/java/com/aliyun/oss/model/DownloadFileRequest.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,46 @@ public int getTrafficLimit() {
235235
return trafficLimit;
236236
}
237237

238+
/**
239+
* Gets the range of the object to download. The range is in the form of
240+
* {start, end}---start and end is position of the object's content.
241+
*
242+
* @return The range of the object to download.
243+
*/
244+
public long[] getRange() {
245+
return range;
246+
}
247+
248+
/**
249+
* Sets the range of the object to download (optional).
250+
*
251+
* @param start
252+
* <p>
253+
* Start position
254+
* </p>
255+
* <p>
256+
* When the start is non-negative, it means the starting position
257+
* to download. When the start is -1, it means the range is
258+
* determined by the end only and the end could not be -1. For
259+
* example, when start is -1 and end is 100. It means the
260+
* download range will be the last 100 bytes.
261+
* </p>
262+
* @param end
263+
* <p>
264+
* End position
265+
* </p>
266+
* <p>
267+
* When the end is non-negative, it means the ending position to
268+
* download. When the end is -1, it means the range is determined
269+
* by the start only and the start could not be -1. For example,
270+
* when end is -1 and start is 100. It means the download range
271+
* will be all exception first 100 bytes.
272+
* </p>
273+
*/
274+
public void setRange(long start, long end) {
275+
range = new long[] { start, end };
276+
}
277+
238278
// Part size in byte, by default it's 100KB.
239279
private long partSize = 1024 * 100;
240280
// Thread count for downloading parts, by default it's 1.
@@ -259,4 +299,6 @@ public int getTrafficLimit() {
259299

260300
// Traffic limit speed, its uint is bit/s
261301
private int trafficLimit;
302+
303+
private long[] range;
262304
}

src/test/java/com/aliyun/oss/integrationtests/DownloadFileTest.java

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
package com.aliyun.oss.integrationtests;
2121

2222
import java.io.File;
23+
import java.util.ArrayList;
24+
import java.util.List;
2325

2426
import junit.framework.Assert;
2527

@@ -171,4 +173,170 @@ public void testUploadFileWithCheckpointFile() {
171173
}
172174
}
173175

176+
static class RangeInfo {
177+
RangeInfo(long start, long end) {
178+
this.start = start;
179+
this.end = end;
180+
}
181+
public long start; // start index;
182+
public long end; // end index;
183+
}
184+
185+
@Test
186+
public void testDownloadFileWithRange() {
187+
final String key = "obj-download-file-range-cp";
188+
189+
try {
190+
File file = createSampleFile(key, 1024 * 500);
191+
192+
// upload file
193+
UploadFileRequest uploadFileRequest = new UploadFileRequest(bucketName, key);
194+
uploadFileRequest.setUploadFile(file.getAbsolutePath());
195+
uploadFileRequest.setTaskNum(10);
196+
uploadFileRequest.setEnableCheckpoint(true);
197+
ObjectMetadata objMetadata = new ObjectMetadata();
198+
objMetadata.addUserMetadata("prop", "propval");
199+
uploadFileRequest.setObjectMetadata(objMetadata);
200+
201+
UploadFileResult uploadRes = ossClient.uploadFile(uploadFileRequest);
202+
Assert.assertEquals(uploadRes.getMultipartUploadResult().getBucketName(), bucketName);
203+
Assert.assertEquals(uploadRes.getMultipartUploadResult().getKey(), key);
204+
205+
//range
206+
List<RangeInfo> rangeInfo = new ArrayList<RangeInfo>();
207+
List<RangeInfo> expectRangeInfo = new ArrayList<RangeInfo>();
208+
// case normal range
209+
rangeInfo.add(new RangeInfo(20, 200*1024 + 100));
210+
expectRangeInfo.add(new RangeInfo(20, 200*1024 + 100));
211+
212+
rangeInfo.add(new RangeInfo(100, 101));
213+
expectRangeInfo.add(new RangeInfo(100, 101));
214+
215+
rangeInfo.add(new RangeInfo(200, 200));
216+
expectRangeInfo.add(new RangeInfo(200, 200));
217+
218+
//to end
219+
rangeInfo.add(new RangeInfo(102402, -1));
220+
expectRangeInfo.add(new RangeInfo(102402, file.length() -1));
221+
222+
//end > size
223+
rangeInfo.add(new RangeInfo(102402, file.length() + 20));
224+
expectRangeInfo.add(new RangeInfo(102402, file.length() -1));
225+
226+
//start < 0, end > 0 && end < size
227+
rangeInfo.add(new RangeInfo(-1, 300*1024 + 100));
228+
expectRangeInfo.add(new RangeInfo(0, 300*1024 + 100));
229+
230+
//start < 0, end > size
231+
rangeInfo.add(new RangeInfo(-1, file.length()));
232+
expectRangeInfo.add(new RangeInfo(0, file.length() -1));
233+
234+
//start < 0, end < 0
235+
rangeInfo.add(new RangeInfo(-1, -1));
236+
expectRangeInfo.add(new RangeInfo(0, file.length() -1));
237+
238+
//start >= size
239+
rangeInfo.add(new RangeInfo(file.length(), file.length() + 20));
240+
expectRangeInfo.add(new RangeInfo(0, file.length() -1));
241+
242+
//start > end
243+
rangeInfo.add(new RangeInfo(1024, 100));
244+
expectRangeInfo.add(new RangeInfo(0, file.length() -1));
245+
246+
Assert.assertEquals(rangeInfo.size(), expectRangeInfo.size());
247+
Assert.assertEquals(true, rangeInfo.size() > 0);
248+
249+
for (int i = 0; i < rangeInfo.size(); i++) {
250+
// download file
251+
String filePathNew = key + "-" + i +"-new.txt";
252+
long start = rangeInfo.get(i).start;
253+
long end = rangeInfo.get(i).end;
254+
DownloadFileRequest downloadFileRequest = new DownloadFileRequest(bucketName, key);
255+
downloadFileRequest.setDownloadFile(filePathNew);
256+
downloadFileRequest.setTaskNum(10);
257+
downloadFileRequest.setEnableCheckpoint(true);
258+
downloadFileRequest.setRange(start, end);
259+
260+
DownloadFileResult downloadRes = ossClient.downloadFile(downloadFileRequest);
261+
262+
ObjectMetadata objMeta = downloadRes.getObjectMetadata();
263+
//Assert.assertEquals(objMeta.getContentLength(), downloadFileRequest.getPartSize());
264+
Assert.assertEquals(objMeta.getObjectType(), "Multipart");
265+
Assert.assertEquals(objMeta.getUserMetadata().get("prop"), "propval");
266+
267+
File fileNew = new File(filePathNew);
268+
long expectStart = expectRangeInfo.get(i).start;
269+
long expectEnd = expectRangeInfo.get(i).end;
270+
Assert.assertTrue("comparte file", compareFileWithRange(file.getAbsolutePath(), expectStart, expectEnd, fileNew.getAbsolutePath()));
271+
fileNew.delete();
272+
}
273+
ossClient.deleteObject(bucketName, key);
274+
} catch (Throwable e) {
275+
e.printStackTrace();
276+
Assert.fail(e.getMessage());
277+
ossClient.deleteBucket(bucketName);
278+
}
279+
}
280+
281+
@Test
282+
public void testDownloadEmptyFileWithoutCheckpoint() {
283+
final String key = "obj-download-empty-file-wcp";
284+
285+
try {
286+
File file = createSampleFile(key, 0);
287+
Assert.assertEquals(0, file.length());
288+
289+
// upload file
290+
UploadFileRequest uploadFileRequest = new UploadFileRequest(bucketName, key);
291+
uploadFileRequest.setUploadFile(file.getAbsolutePath());
292+
uploadFileRequest.setTaskNum(10);
293+
ObjectMetadata objMetadata = new ObjectMetadata();
294+
objMetadata.addUserMetadata("prop", "propval");
295+
uploadFileRequest.setObjectMetadata(objMetadata);
296+
297+
UploadFileResult uploadRes = ossClient.uploadFile(uploadFileRequest);
298+
Assert.assertEquals(uploadRes.getMultipartUploadResult().getBucketName(), bucketName);
299+
Assert.assertEquals(uploadRes.getMultipartUploadResult().getKey(), key);
300+
301+
// download file
302+
String filePathNew = key + "-new.txt";
303+
DownloadFileRequest downloadFileRequest = new DownloadFileRequest(bucketName, key);
304+
downloadFileRequest.setDownloadFile(filePathNew);
305+
downloadFileRequest.setTaskNum(10);
306+
307+
DownloadFileResult downloadRes = ossClient.downloadFile(downloadFileRequest);
308+
309+
ObjectMetadata objMeta = downloadRes.getObjectMetadata();
310+
Assert.assertEquals(objMeta.getContentLength(), 0);
311+
Assert.assertEquals(objMeta.getObjectType(), "Multipart");
312+
Assert.assertEquals(objMeta.getUserMetadata().get("prop"), "propval");
313+
314+
File fileNew = new File(filePathNew);
315+
Assert.assertEquals(0, fileNew.length());
316+
fileNew.delete();
317+
318+
//download file with range
319+
filePathNew = key + "range-new.txt";
320+
downloadFileRequest = new DownloadFileRequest(bucketName, key);
321+
downloadFileRequest.setDownloadFile(filePathNew);
322+
downloadFileRequest.setTaskNum(10);
323+
downloadFileRequest.setRange(0, 0);
324+
downloadRes = ossClient.downloadFile(downloadFileRequest);
325+
326+
objMeta = downloadRes.getObjectMetadata();
327+
Assert.assertEquals(objMeta.getContentLength(), 0);
328+
Assert.assertEquals(objMeta.getObjectType(), "Multipart");
329+
Assert.assertEquals(objMeta.getUserMetadata().get("prop"), "propval");
330+
331+
fileNew = new File(filePathNew);
332+
Assert.assertEquals(0, fileNew.length());
333+
fileNew.delete();
334+
335+
ossClient.deleteObject(bucketName, key);
336+
} catch (Throwable e) {
337+
e.printStackTrace();
338+
Assert.fail(e.getMessage());
339+
ossClient.deleteBucket(bucketName);
340+
}
341+
}
174342
}

0 commit comments

Comments
 (0)