Skip to content

Commit

Permalink
Merge pull request #702 from saurabytes/FixS3PreSignedUpload
Browse files Browse the repository at this point in the history
Fix: S3 pre signed uploads or similar binary stream uploads writing corrupt files
  • Loading branch information
itinance authored Jul 16, 2019
2 parents a971519 + 8eec4e8 commit 4d9f260
Show file tree
Hide file tree
Showing 10 changed files with 72 additions and 38 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,5 @@ android/.gradle/*
android/gradle/*
android/*.iml
android/local.properties
android/.settings
android/.project
2 changes: 2 additions & 0 deletions FS.common.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ type DownloadResult = {

type UploadFileOptions = {
toUrl: string; // URL to upload file to
binaryStreamOnly?: boolean; // Allow for binary data stream for file to be uploaded without extra headers, Default is 'false'
files: UploadFileItem[]; // An array of objects with the file information to be uploaded.
headers?: Headers; // An object of headers to be passed to the server
fields?: Fields; // An object of fields to be passed to the server
Expand Down Expand Up @@ -565,6 +566,7 @@ var RNFS = {
jobId: jobId,
toUrl: options.toUrl,
files: options.files,
binaryStreamOnly: options.binaryStreamOnly || false,
headers: options.headers || {},
fields: options.fields || {},
method: options.method || 'POST'
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,7 @@ Read more about background downloads in the [Background Downloads Tutorial (iOS)
```
type UploadFileOptions = {
toUrl: string; // URL to upload file to
binaryStreamOnly?: boolean// Allow for binary data stream for file to be uploaded without extra headers, Default is 'false'
files: UploadFileItem[]; // An array of objects with the file information to be uploaded.
headers?: Headers; // An object of headers to be passed to the server
fields?: Fields; // An object of fields to be passed to the server
Expand Down
1 change: 1 addition & 0 deletions RNFSManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,7 @@ + (BOOL)requiresMainQueueSetup
NSNumber* jobId = options[@"jobId"];
params.toUrl = options[@"toUrl"];
params.files = options[@"files"];
params.binaryStreamOnly = options[@"binaryStreamOnly"];
NSDictionary* headers = options[@"headers"];
NSDictionary* fields = options[@"fields"];
NSString* method = options[@"method"];
Expand Down
3 changes: 2 additions & 1 deletion Uploader.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ typedef void (^UploadProgressCallback)(NSNumber*, NSNumber*);
@property (copy) NSDictionary* headers;
@property (copy) NSDictionary* fields;
@property (copy) NSString* method;
@property (copy) UploadCompleteCallback completeCallback; // Upload has finished (data written)
@property (assign) BOOL binaryStreamOnly;
@property (copy) UploadCompleteCallback completeCallback; // Upload has finished (data written)
@property (copy) UploadErrorCallback errorCallback; // Something gone wrong
@property (copy) UploadBeginCallback beginCallback; // Upload has started
@property (copy) UploadProgressCallback progressCallback; // Upload is progressing
Expand Down
38 changes: 22 additions & 16 deletions Uploader.m
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ - (void)uploadFiles:(RNFSUploadParams*)params
NSURL *url = [NSURL URLWithString:_params.toUrl];
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
[req setHTTPMethod:method];
BOOL binaryStreamOnly = _params.binaryStreamOnly;

// set headers
NSString *formBoundaryString = [self generateBoundaryString];
Expand Down Expand Up @@ -56,9 +57,9 @@ - (void)uploadFiles:(RNFSUploadParams*)params
[reqBody appendData:[val dataUsingEncoding:NSUTF8StringEncoding]];
[reqBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
}

NSArray *files = _params.files;
// add files
for (NSDictionary *file in _params.files) {
for (NSDictionary *file in files) {
NSString *name = file[@"name"];
NSString *filename = file[@"filename"];
NSString *filepath = file[@"filepath"];
Expand All @@ -74,24 +75,29 @@ - (void)uploadFiles:(RNFSUploadParams*)params
}

NSData *fileData = [NSData dataWithContentsOfFile:filepath];

[reqBody appendData:formBoundaryData];
[reqBody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", name.length ? name : filename, filename] dataUsingEncoding:NSUTF8StringEncoding]];

if (filetype) {
[reqBody appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n", filetype] dataUsingEncoding:NSUTF8StringEncoding]];
} else {
[reqBody appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n", [self mimeTypeForPath:filename]] dataUsingEncoding:NSUTF8StringEncoding]];
if (!binaryStreamOnly) {
[reqBody appendData:formBoundaryData];
[reqBody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", name.length ? name : filename, filename] dataUsingEncoding:NSUTF8StringEncoding]];

if (filetype) {
[reqBody appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n", filetype] dataUsingEncoding:NSUTF8StringEncoding]];
} else {
[reqBody appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n", [self mimeTypeForPath:filename]] dataUsingEncoding:NSUTF8StringEncoding]];
}
[reqBody appendData:[[NSString stringWithFormat:@"Content-Length: %ld\r\n\r\n", (long)[fileData length]] dataUsingEncoding:NSUTF8StringEncoding]];
}

[reqBody appendData:[[NSString stringWithFormat:@"Content-Length: %ld\r\n\r\n", (long)[fileData length]] dataUsingEncoding:NSUTF8StringEncoding]];

[reqBody appendData:fileData];
[reqBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
if (!binaryStreamOnly) {
[reqBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
}
}

// add end boundary
NSData* end = [[NSString stringWithFormat:@"--%@--\r\n", formBoundaryString] dataUsingEncoding:NSUTF8StringEncoding];
[reqBody appendData:end];
if (!binaryStreamOnly) {
// add end boundary
NSData* end = [[NSString stringWithFormat:@"--%@--\r\n", formBoundaryString] dataUsingEncoding:NSUTF8StringEncoding];
[reqBody appendData:end];
}

// send request
[req setHTTPBody:reqBody];
Expand Down
6 changes: 4 additions & 2 deletions android/src/main/java/com/rnfs/RNFSManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -788,6 +788,7 @@ public void uploadFiles(final ReadableMap options, final Promise promise) {
ReadableMap headers = options.getMap("headers");
ReadableMap fields = options.getMap("fields");
String method = options.getString("method");
boolean binaryStreamOnly = options.getBoolean("binaryStreamOnly");
ArrayList<ReadableMap> fileList = new ArrayList<>();
UploadParams params = new UploadParams();
for(int i =0;i<files.size();i++){
Expand All @@ -796,8 +797,9 @@ public void uploadFiles(final ReadableMap options, final Promise promise) {
params.src = url;
params.files =fileList;
params.headers = headers;
params.method=method;
params.fields=fields;
params.method = method;
params.fields = fields;
params.binaryStreamOnly = binaryStreamOnly;
params.onUploadComplete = new UploadParams.onUploadComplete() {
public void onUploadComplete(UploadResult res) {
if (res.exception == null) {
Expand Down
1 change: 1 addition & 0 deletions android/src/main/java/com/rnfs/UploadParams.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public interface onUploadBegin{
}
public URL src;
public ArrayList<ReadableMap> files;
public boolean binaryStreamOnly;
public String name;
public ReadableMap headers;
public ReadableMap fields;
Expand Down
55 changes: 36 additions & 19 deletions android/src/main/java/com/rnfs/Uploader.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,16 @@ private void upload(UploadParams params, UploadResult result) throws Exception {
BufferedReader responseStreamReader = null;
String name, filename, filetype;
try {
Object[] files = params.files.toArray();
boolean binaryStreamOnly = params.binaryStreamOnly;

connection = (HttpURLConnection) params.src.openConnection();
connection.setDoOutput(true);
ReadableMapKeySetIterator headerIterator = params.headers.keySetIterator();
connection.setRequestMethod(params.method);
connection.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);
if (!binaryStreamOnly) {
connection.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);
}
while (headerIterator.hasNextKey()) {
String key = headerIterator.nextKey();
String value = params.headers.getString(key);
Expand All @@ -79,7 +84,7 @@ private void upload(UploadParams params, UploadResult result) throws Exception {
metaData += twoHyphens + boundary + crlf + "Content-Disposition: form-data; name=\"" + key + "\"" + crlf + crlf + value +crlf;
}
stringData += metaData;
fileHeader = new String[params.files.toArray().length];
fileHeader = new String[files.length];
for (ReadableMap map : params.files) {
try {
name = map.getString("name");
Expand All @@ -91,34 +96,45 @@ private void upload(UploadParams params, UploadResult result) throws Exception {
filetype = getMimeType(map.getString("filepath"));
}
File file = new File(map.getString("filepath"));
String fileHeaderType = twoHyphens + boundary + crlf +
"Content-Disposition: form-data; name=\"" + name + "\"; filename=\"" + filename + "\"" + crlf +
"Content-Type: " + filetype + crlf;
long fileLength = file.length();
totalFileLength += fileLength ;
if(params.files.toArray().length - 1 == fileCount){
totalFileLength += tail.length();
totalFileLength += fileLength;
if (!binaryStreamOnly) {
String fileHeaderType = twoHyphens + boundary + crlf +
"Content-Disposition: form-data; name=\"" + name + "\"; filename=\"" + filename + "\"" + crlf +
"Content-Type: " + filetype + crlf;
;
if (files.length - 1 == fileCount){
totalFileLength += tail.length();
}

String fileLengthHeader = "Content-length: " + fileLength + crlf;
fileHeader[fileCount] = fileHeaderType + fileLengthHeader + crlf;
stringData += fileHeaderType + fileLengthHeader + crlf;
}
String fileLengthHeader = "Content-length: " + fileLength + crlf;
fileHeader[fileCount] = fileHeaderType + fileLengthHeader + crlf;
stringData += fileHeaderType + fileLengthHeader + crlf;
fileCount++;
}
fileCount = 0;
mParams.onUploadBegin.onUploadBegin();
long requestLength = totalFileLength + stringData.length() + params.files.toArray().length * crlf.length();
connection.setRequestProperty("Content-length", "" +(int) requestLength);
connection.setFixedLengthStreamingMode((int)requestLength);
if (!binaryStreamOnly) {
long requestLength = totalFileLength;
requestLength += stringData.length() + files.length * crlf.length();
connection.setRequestProperty("Content-length", "" +(int) requestLength);
connection.setFixedLengthStreamingMode((int)requestLength);
}
connection.connect();

request = new DataOutputStream(connection.getOutputStream());
request.writeBytes(metaData);
if (!binaryStreamOnly) {
request.writeBytes(metaData);
}

byteSentTotal = 0;
Runtime run = Runtime.getRuntime();

for (ReadableMap map : params.files) {
request.writeBytes(fileHeader[fileCount]);
if (!binaryStreamOnly) {
request.writeBytes(fileHeader[fileCount]);
}
File file = new File(map.getString("filepath"));
int fileLength = (int) file.length();
int bytes_read = 0;
Expand All @@ -131,9 +147,11 @@ private void upload(UploadParams params, UploadResult result) throws Exception {
while ((bytes_read = bufInput.read(buffer)) != -1) {
request.write(buffer, 0, bytes_read);
byteSentTotal += bytes_read;
mParams.onUploadProgress.onUploadProgress((int) totalFileLength - tail.length(), byteSentTotal);
mParams.onUploadProgress.onUploadProgress((int) totalFileLength, byteSentTotal);
}
if (!binaryStreamOnly) {
request.writeBytes(crlf);
}
request.writeBytes(crlf);
fileCount++;
bufInput.close();
}
Expand Down Expand Up @@ -188,5 +206,4 @@ protected String getMimeType(String path) {
protected void stop() {
mAbort.set(true);
}

}
1 change: 1 addition & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ type DownloadResult = {

type UploadFileOptions = {
toUrl: string // URL to upload file to
binaryStreamOnly?: boolean // Allow for binary data stream for file to be uploaded without extra headers, Default is 'false'
files: UploadFileItem[] // An array of objects with the file information to be uploaded.
headers?: Headers // An object of headers to be passed to the server
fields?: Fields // An object of fields to be passed to the server
Expand Down

0 comments on commit 4d9f260

Please sign in to comment.