Skip to content

Commit e0dbff6

Browse files
authored
[Bug]Fixing missing bytes when processing chunks of a file during upload (#1941)
fix: missing bytes when processing chunks of a file during upload * Fixing spot bug issue
1 parent 67b758a commit e0dbff6

File tree

2 files changed

+89
-12
lines changed

2 files changed

+89
-12
lines changed

src/main/java/com/microsoft/graph/core/tasks/LargeFileUploadTask.java

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,20 @@
11
package com.microsoft.graph.core.tasks;
22

3+
import java.io.ByteArrayInputStream;
4+
import java.io.IOException;
5+
import java.io.InputStream;
6+
import java.lang.reflect.InvocationTargetException;
7+
import java.time.OffsetDateTime;
8+
import java.util.AbstractMap;
9+
import java.util.ArrayList;
10+
import java.util.List;
11+
import java.util.Locale;
12+
import java.util.Map;
13+
import java.util.Objects;
14+
import java.util.concurrent.CancellationException;
15+
import java.util.concurrent.TimeUnit;
16+
import java.util.function.Consumer;
17+
318
import com.microsoft.graph.core.ErrorConstants;
419
import com.microsoft.graph.core.exceptions.ClientException;
520
import com.microsoft.graph.core.models.IProgressCallback;
@@ -18,19 +33,10 @@
1833
import com.microsoft.kiota.serialization.Parsable;
1934
import com.microsoft.kiota.serialization.ParsableFactory;
2035
import com.microsoft.kiota.serialization.ParseNode;
21-
import okhttp3.OkHttpClient;
2236

2337
import jakarta.annotation.Nonnull;
2438
import jakarta.annotation.Nullable;
25-
import java.io.ByteArrayInputStream;
26-
import java.io.IOException;
27-
import java.io.InputStream;
28-
import java.lang.reflect.InvocationTargetException;
29-
import java.time.OffsetDateTime;
30-
import java.util.*;
31-
import java.util.concurrent.CancellationException;
32-
import java.util.concurrent.TimeUnit;
33-
import java.util.function.Consumer;
39+
import okhttp3.OkHttpClient;
3440

3541
/**
3642
* Task for uploading large files including pausing and resuming.
@@ -274,8 +280,15 @@ private long nextSliceSize(long rangeBegin, long rangeEnd) {
274280
}
275281
private byte[] chunkInputStream(InputStream stream, int length) throws IOException {
276282
byte[] buffer = new byte[length];
277-
int lengthAssert = stream.read(buffer);
278-
assert lengthAssert == length;
283+
int totalRead = 0;
284+
while (totalRead < length) {
285+
int bytesRead = stream.read(buffer, totalRead, length - totalRead);
286+
if (bytesRead == -1) {
287+
// End of stream reached
288+
break;
289+
}
290+
totalRead += bytesRead;
291+
}
279292
return buffer;
280293
}
281294
}

src/test/java/com/microsoft/graph/core/tasks/LargeFileUploadTest.java

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,14 @@
55
import com.microsoft.graph.core.models.UploadSession;
66
import com.microsoft.kiota.authentication.AuthenticationProvider;
77
import com.microsoft.kiota.http.OkHttpRequestAdapter;
8+
89
import org.junit.jupiter.api.Test;
910
import static org.mockito.Mockito.mock;
11+
import static org.mockito.Mockito.spy;
12+
import static org.mockito.Mockito.doReturn;
1013
import static org.junit.jupiter.api.Assertions.assertEquals;
14+
import static org.junit.jupiter.api.Assertions.assertTrue;
15+
import org.mockito.ArgumentCaptor;
1116

1217
import java.io.ByteArrayInputStream;
1318
import java.io.IOException;
@@ -17,6 +22,10 @@
1722
import java.util.ArrayList;
1823
import java.util.Arrays;
1924

25+
import org.mockito.internal.matchers.Any;
26+
27+
import com.microsoft.graph.core.models.UploadResult;
28+
2029
class LargeFileUploadTest {
2130

2231
final OkHttpRequestAdapter adapter = new OkHttpRequestAdapter(mock(AuthenticationProvider.class));
@@ -106,4 +115,59 @@ void BreakStreamIntoCorrectRanges() throws IOException, NoSuchFieldException, Il
106115
assertEquals(size%maxSliceSize, lastSlice.getRangeLength());
107116
assertEquals(size-1, lastSlice.getRangeEnd());
108117
}
118+
// Test for chunkInputStream method with a 5MB file
119+
@Test
120+
void uploads5MBFileSuccessfully() throws Exception {
121+
// Arrange
122+
UploadSession session = new UploadSession();
123+
session.setNextExpectedRanges(Arrays.asList("0-"));
124+
session.setUploadUrl("http://localhost");
125+
session.setExpirationDateTime(OffsetDateTime.now().plusHours(1));
126+
127+
// 5MB file
128+
byte[] data = new byte[5 * 1024 * 1024];
129+
for (int i = 0; i < data.length; i++) {
130+
data[i] = (byte)(i % 256);
131+
}
132+
ByteArrayInputStream stream = new ByteArrayInputStream(data);
133+
int size = stream.available();
134+
135+
// Create a real task to get the real builder(s)
136+
LargeFileUploadTask<TestDriveItem> realTask = new LargeFileUploadTask<>(adapter, session, stream, size, TestDriveItem::createFromDiscriminatorValue);
137+
var realBuilders = realTask.getUploadSliceRequests();
138+
139+
// Spy the builder(s) and mock put()
140+
ArrayList<UploadSliceRequestBuilder<TestDriveItem>> spyBuilders = new ArrayList<>();
141+
ArgumentCaptor<ByteArrayInputStream> captor = ArgumentCaptor.forClass(ByteArrayInputStream.class);
142+
143+
for (UploadSliceRequestBuilder<TestDriveItem> builder : realBuilders) {
144+
UploadSliceRequestBuilder<TestDriveItem> spyBuilder = spy(builder);
145+
UploadResult<TestDriveItem> mockResult = new UploadResult<>();
146+
TestDriveItem item = new TestDriveItem();
147+
item.size = data.length;
148+
mockResult.itemResponse = item;
149+
doReturn(mockResult).when(spyBuilder).put(captor.capture());
150+
spyBuilders.add(spyBuilder);
151+
}
152+
153+
// Subclass LargeFileUploadTask to inject our spy builders
154+
LargeFileUploadTask<TestDriveItem> task = new LargeFileUploadTask<>(adapter, session, stream, size, TestDriveItem::createFromDiscriminatorValue) {
155+
@Override
156+
protected java.util.List<UploadSliceRequestBuilder<TestDriveItem>> getUploadSliceRequests() {
157+
return spyBuilders;
158+
}
159+
};
160+
161+
// Act
162+
task.upload(3, null);
163+
164+
// Verify the chunkStream content
165+
ByteArrayInputStream capturedStream = captor.getValue();
166+
byte[] capturedBytes = new byte[data.length];
167+
int read = capturedStream.read(capturedBytes);
168+
assertEquals(data.length, read, "Should read all bytes from chunkStream");
169+
for (int i = 0; i < data.length; i++) {
170+
assertEquals(data[i], capturedBytes[i], "Byte at position " + i + " should match original data");
171+
}
172+
}
109173
}

0 commit comments

Comments
 (0)