Skip to content

Commit 46ea0ec

Browse files
authored
Merge pull request #482 from qiniu/etag_v2
Etag v2
2 parents 949ec33 + 4d97663 commit 46ea0ec

File tree

4 files changed

+144
-12
lines changed

4 files changed

+144
-12
lines changed

src/main/java/com/qiniu/storage/FixBlockUploader.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,12 @@ public class FixBlockUploader {
2929
private String host = null;
3030

3131
/**
32-
* @param blockSize must be multiples of 4M.
32+
* @param blockSize block size, eg: 1024 * 1024 * 8.
3333
* @param configuration Nullable, if null, then create a new one.
3434
* @param client Nullable, if null, then create a new one with configuration.
3535
* @param recorder Nullable.
3636
*/
3737
public FixBlockUploader(int blockSize, Configuration configuration, Client client, Recorder recorder) {
38-
assert blockSize > 0 && blockSize % (4 * 1024 * 1024) == 0 : "blockSize must be multiples of 4M ";
39-
4038
if (configuration == null) {
4139
configuration = new Configuration();
4240
}
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
package com.qiniu.util;
2+
3+
import java.io.*;
4+
import java.security.MessageDigest;
5+
import java.security.NoSuchAlgorithmException;
6+
import java.util.Arrays;
7+
8+
public class EtagV2 {
9+
private EtagV2() {
10+
}
11+
12+
public static String data(byte[] data, int offset, int length, long blockSize) {
13+
try {
14+
return stream(new ByteArrayInputStream(data, offset, length), length, blockSize);
15+
} catch (IOException e) {
16+
throw new AssertionError(e);
17+
}
18+
}
19+
20+
public static String data(byte[] data, long blockSize) {
21+
return data(data, 0, data.length, blockSize);
22+
}
23+
24+
public static String data(byte[] data, int offset, int length, long[] parts) {
25+
try {
26+
return stream(new ByteArrayInputStream(data, offset, length), length, parts);
27+
} catch (IOException e) {
28+
throw new AssertionError(e);
29+
}
30+
}
31+
32+
public static String data(byte[] data, long[] parts) {
33+
return data(data, 0, data.length, parts);
34+
}
35+
36+
public static String file(String filePath, long blockSize) throws IOException {
37+
return file(new File(filePath), blockSize);
38+
}
39+
40+
public static String file(String filePath, long[] parts) throws IOException {
41+
return file(new File(filePath), parts);
42+
}
43+
44+
public static String file(File file, long blockSize) throws IOException {
45+
FileInputStream fi = null;
46+
try {
47+
fi = new FileInputStream(file);
48+
return stream(fi, file.length(), blockSize);
49+
} finally {
50+
if (fi != null) {
51+
try {
52+
fi.close();
53+
} catch (Throwable t) {
54+
}
55+
}
56+
}
57+
}
58+
59+
public static String file(File file, long[] parts) throws IOException {
60+
FileInputStream fi = null;
61+
try {
62+
fi = new FileInputStream(file);
63+
return stream(fi, file.length(), parts);
64+
} finally {
65+
if (fi != null) {
66+
try {
67+
fi.close();
68+
} catch (Throwable t) {
69+
}
70+
}
71+
}
72+
}
73+
74+
75+
public static String stream(InputStream in, long len, long blockSize) throws IOException {
76+
// // 和 is4MBParts(new long[]{len}) 实质一样,只有一个 part 的简化版 // //
77+
if (blockSize == 1024 * 1024 * 4 || (len <= blockSize && len <= 1024 * 1024 * 4)) {
78+
return Etag.stream(in, len);
79+
}
80+
int size = (int) ((len + blockSize - 1) / blockSize);
81+
long[] parts = new long[size];
82+
Arrays.fill(parts, 0, size - 1, blockSize);
83+
parts[size - 1] = len % blockSize;
84+
85+
return etagV2(in, len, parts);
86+
}
87+
88+
public static String stream(InputStream in, long len, long[] parts) throws IOException {
89+
if (is4MBParts(parts)) {
90+
return Etag.stream(in, len);
91+
}
92+
93+
return etagV2(in, len, parts);
94+
}
95+
96+
private static boolean is4MBParts(long[] parts) {
97+
int idx = 0;
98+
int last = parts.length - 1;
99+
for (long part : parts) {
100+
if (idx != last && part != 1024 * 1024 * 4 || part > 1024 * 1024 * 4) {
101+
return false;
102+
}
103+
idx += 1;
104+
}
105+
return true;
106+
}
107+
108+
private static String etagV2(InputStream in, long len, long[] parts) throws IOException {
109+
long partSize = 0;
110+
for (long part : parts) {
111+
partSize += part;
112+
}
113+
if (len != partSize) {
114+
throw new IOException("etag calc failed: size not equal with part size");
115+
}
116+
117+
MessageDigest sha1;
118+
try {
119+
sha1 = MessageDigest.getInstance("sha-1");
120+
} catch (NoSuchAlgorithmException e) {
121+
throw new AssertionError(e);
122+
}
123+
for (long part : parts) {
124+
String partEtag = Etag.stream(in, part);
125+
byte[] bytes = UrlSafeBase64.decode(partEtag);
126+
sha1.update(bytes, 1, bytes.length - 1);
127+
}
128+
byte[] digest = sha1.digest();
129+
byte[] ret = new byte[digest.length + 1];
130+
ret[0] = (byte) 0x9e;
131+
System.arraycopy(digest, 0, ret, 1, digest.length);
132+
return UrlSafeBase64.encodeToString(ret);
133+
}
134+
}

src/test/java/com/qiniu/storage/FixBlockUploaderWithRecorderTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import com.qiniu.http.Client;
66
import com.qiniu.http.Response;
77
import com.qiniu.storage.persistent.FileRecorder;
8-
import com.qiniu.util.Etag;
8+
import com.qiniu.util.EtagV2;
99
import com.qiniu.util.StringMap;
1010
import com.qiniu.util.UrlSafeBase64;
1111
import org.junit.Before;
@@ -96,7 +96,7 @@ public void breakThenUpload(final ExecutorService pool1, final ExecutorService p
9696
final File f = TempFile.createFileOld(size);
9797
final FixBlockUploader.FileBlockData fbd = new FixBlockUploader.FileBlockData(blockSize, f);
9898
System.out.println(f.getAbsolutePath());
99-
final String etag = Etag.file(f);
99+
final String etag = EtagV2.file(f, blockSize);
100100
final String returnBody = "{\"key\":\"$(key)\",\"hash\":\"$(etag)\",\"fsize\":\"$(fsize)\""
101101
+ ",\"fname\":\"$(fname)\",\"mimeType\":\"$(mimeType)\"}";
102102

src/test/java/test/com/qiniu/storage/FixBlockUploaderTest.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import com.qiniu.storage.BucketManager;
88
import com.qiniu.storage.Configuration;
99
import com.qiniu.storage.FixBlockUploader;
10-
import com.qiniu.util.Etag;
10+
import com.qiniu.util.EtagV2;
1111
import com.qiniu.util.StringMap;
1212
import org.junit.Assert;
1313
import org.junit.Before;
@@ -24,7 +24,7 @@
2424
import static org.junit.Assert.assertEquals;
2525

2626
public class FixBlockUploaderTest {
27-
int blockSize = 1024 * 1024 * 8;
27+
int blockSize = 1024 * 1021 * 7;
2828
Configuration config;
2929
Client client;
3030
FixBlockUploader up;
@@ -147,7 +147,7 @@ private void template(boolean isStream, int size, boolean https, boolean fixFile
147147
f = TempFile.createFileOld(size);
148148
}
149149
System.out.println(f.getAbsolutePath());
150-
final String etag = Etag.file(f);
150+
final String etag = EtagV2.file(f, blockSize);
151151
System.out.println(etag);
152152
final String returnBody = "{\"key\":\"$(key)\",\"hash\":\"$(etag)\",\"fsize\":\"$(fsize)\""
153153
+ ",\"fname\":\"$(fname)\",\"mimeType\":\"$(mimeType)\"}";
@@ -200,7 +200,7 @@ private void template(boolean isStream, int size, boolean https, boolean fixFile
200200
@Test
201201
public void testEmptyKey() throws IOException {
202202
File f = TempFile.createFileOld(1);
203-
String etag = Etag.file(f);
203+
String etag = EtagV2.file(f, blockSize);
204204
String token = TestConfig.testAuth.uploadToken(bucket, null);
205205
Response res = up.upload(f, token, "");
206206
System.out.println(res.getInfo());
@@ -212,7 +212,7 @@ public void testEmptyKey() throws IOException {
212212
@Test
213213
public void testNullKey() throws IOException {
214214
File f = TempFile.createFile(2);
215-
String etag = Etag.file(f);
215+
String etag = EtagV2.file(f, blockSize);
216216
String token = TestConfig.testAuth.uploadToken(bucket, null);
217217
Response res = up.upload(f, token, null);
218218
System.out.println(res.getInfo());
@@ -224,7 +224,7 @@ public void testNullKey() throws IOException {
224224
@Test
225225
public void testKey2() throws IOException {
226226
File f = TempFile.createFile(2);
227-
String etag = Etag.file(f);
227+
String etag = EtagV2.file(f, blockSize);
228228
String token = TestConfig.testAuth.uploadToken(bucket, "err");
229229
try {
230230
Response res = up.upload(f, token, null);
@@ -238,7 +238,7 @@ public void testKey2() throws IOException {
238238
@Test
239239
public void testMeat() throws IOException {
240240
File f = TempFile.createFile(1);
241-
String etag = Etag.file(f);
241+
String etag = EtagV2.file(f, blockSize);
242242
String returnBody = "{\"key\":\"$(key)\",\"hash\":\"$(etag)\",\"fsize\":\"$(fsize)\""
243243
+ ",\"fname\":\"$(x:biubiu)_$(fname)\",\"mimeType\":\"$(mimeType)\",\"biu2biu\":\"$(x:biu2biu)\"}";
244244

0 commit comments

Comments
 (0)