Skip to content

Commit 9307179

Browse files
committed
Add FileUploader plugin for iPhone
1 parent 072a8a0 commit 9307179

File tree

4 files changed

+388
-0
lines changed

4 files changed

+388
-0
lines changed

iPhone/FileUploader/FileUploader.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//
2+
// FileUploader.h
3+
//
4+
// Created by Matt Kane on 14/01/2011.
5+
// Copyright 2011 Matt Kane. All rights reserved.
6+
//
7+
8+
#import <Foundation/Foundation.h>
9+
#import "PhoneGapCommand.h"
10+
11+
@interface FileUploader : PhoneGapCommand {
12+
13+
}
14+
- (void) upload:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
15+
- (void) uploadByUri:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
16+
- (void) uploadFile:(NSURL*)file toServer:(NSString*)server withParams:(NSMutableDictionary*)params fileKey:(NSString*)fileKey fileName:(NSString*)fileName mimeType:(NSString*)mimeType successCallback:(NSString*)successCallback failCallback:(NSString*)failCallback progressCallback:(NSString*)progressCallback;
17+
@end
18+
19+
20+
@interface FileUploadDelegate : NSObject {
21+
NSString* successCallback;
22+
NSString* failCallback;
23+
NSString* progressCallback;
24+
FileUploader* command;
25+
int uploadIdx;
26+
}
27+
28+
@property (nonatomic, copy) NSString* successCallback;
29+
@property (nonatomic, copy) NSString* failCallback;
30+
@property (nonatomic, copy) NSString* progressCallback;
31+
@property (nonatomic, retain) NSMutableData* responseData;
32+
@property (nonatomic, retain) FileUploader* command;
33+
34+
@end;

iPhone/FileUploader/FileUploader.m

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
//
2+
// FileUploader.m
3+
//
4+
// Created by Matt Kane on 14/01/2011.
5+
// Copyright 2011 Matt Kane. All rights reserved.
6+
//
7+
8+
#import "FileUploader.h"
9+
10+
11+
@implementation FileUploader
12+
13+
- (void) upload:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
14+
{
15+
NSUInteger argc = [arguments count];
16+
17+
if(argc < 2) {
18+
return;
19+
}
20+
21+
NSString* successCallback = [arguments objectAtIndex:0];
22+
NSString* failCallback = [arguments objectAtIndex:1];
23+
24+
if(argc < 6) {
25+
[self writeJavascript: [NSString stringWithFormat:@"%@(\"Argument error\");", failCallback]];
26+
return;
27+
}
28+
29+
NSString* progressCallback = [arguments objectAtIndex:2];
30+
NSString* server = [arguments objectAtIndex:3];
31+
NSURL* file = [NSURL fileURLWithPath:[arguments objectAtIndex:4] isDirectory: NO];
32+
NSString* fileKey = nil;
33+
NSString* fileName = nil;
34+
NSString* mimeType = nil;
35+
36+
if(argc > 5) {
37+
fileKey = [arguments objectAtIndex:5];
38+
}
39+
40+
if(argc > 6) {
41+
fileName = [arguments objectAtIndex:6];
42+
}
43+
44+
if(argc > 7) {
45+
mimeType = [arguments objectAtIndex:7];
46+
}
47+
[self uploadFile:file toServer:server withParams:options fileKey:fileKey fileName:fileName mimeType:mimeType successCallback:successCallback failCallback:failCallback progressCallback:progressCallback];
48+
}
49+
50+
- (void) uploadByUri:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
51+
{
52+
NSUInteger argc = [arguments count];
53+
54+
if(argc < 2) {
55+
return;
56+
}
57+
58+
NSString* successCallback = [arguments objectAtIndex:0];
59+
NSString* failCallback = [arguments objectAtIndex:1];
60+
61+
if(argc < 6) {
62+
[self writeJavascript: [NSString stringWithFormat:@"%@(\"Argument error\");", failCallback]];
63+
return;
64+
}
65+
NSString* progressCallback = [arguments objectAtIndex:2];
66+
NSString* server = [arguments objectAtIndex:3];
67+
NSURL* file = [NSURL URLWithString:[arguments objectAtIndex:4]];
68+
NSString* fileKey = nil;
69+
NSString* fileName = nil;
70+
NSString* mimeType = nil;
71+
72+
if(argc > 5) {
73+
fileKey = [arguments objectAtIndex:5];
74+
}
75+
76+
if(argc > 6) {
77+
fileName = [arguments objectAtIndex:6];
78+
}
79+
80+
if(argc > 7) {
81+
mimeType = [arguments objectAtIndex:7];
82+
}
83+
[self uploadFile:file toServer:server withParams:options fileKey:fileKey fileName:fileName mimeType:mimeType successCallback:successCallback failCallback:failCallback progressCallback:progressCallback];
84+
}
85+
86+
- (void) uploadFile:(NSURL*)file toServer:(NSString*)server withParams:(NSMutableDictionary*)params fileKey:(NSString*)fileKey fileName:(NSString*)fileName mimeType:(NSString*)mimeType successCallback:(NSString*)successCallback failCallback:(NSString*)failCallback progressCallback:(NSString*)progressCallback
87+
{
88+
89+
if (![file isFileURL]) {
90+
[self writeJavascript: [NSString stringWithFormat:@"%@(\"Is not a valid file\");", failCallback]];
91+
return;
92+
}
93+
94+
if(!fileName) {
95+
fileName = @"image.jpg";
96+
}
97+
98+
if(!mimeType) {
99+
mimeType = @"image/jpeg";
100+
}
101+
102+
if(!fileKey) {
103+
fileKey = @"file";
104+
}
105+
106+
NSString *boundary = @"*****com.beetight.formBoundary";
107+
108+
NSURL *url = [NSURL URLWithString:server];
109+
110+
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
111+
[req setHTTPMethod:@"POST"];
112+
113+
NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary];
114+
[req setValue:contentType forHTTPHeaderField:@"Content-type"];
115+
NSString* userAgent = [[webView request] valueForHTTPHeaderField:@"User-agent"];
116+
if(userAgent) {
117+
[req setValue: userAgent forHTTPHeaderField:@"User-agent"];
118+
}
119+
120+
NSData *imageData = [NSData dataWithContentsOfURL:file];
121+
122+
if(!imageData) {
123+
[self writeJavascript: [NSString stringWithFormat:@"%@(\"Could not open file\");", failCallback]];
124+
return;
125+
}
126+
127+
NSMutableData *postBody = [NSMutableData data];
128+
129+
NSEnumerator *enumerator = [params keyEnumerator];
130+
id key;
131+
id val;
132+
while ((key = [enumerator nextObject])) {
133+
val = [params objectForKey:key];
134+
if(!val || val == [NSNull null]) {
135+
continue;
136+
}
137+
[postBody appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
138+
[postBody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", key] dataUsingEncoding:NSUTF8StringEncoding]];
139+
[postBody appendData:[val dataUsingEncoding:NSUTF8StringEncoding]];
140+
[postBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
141+
}
142+
143+
[postBody appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
144+
[postBody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", fileKey, fileName] dataUsingEncoding:NSUTF8StringEncoding]];
145+
[postBody appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n\r\n", mimeType] dataUsingEncoding:NSUTF8StringEncoding]];
146+
[postBody appendData:imageData];
147+
[postBody appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
148+
[req setHTTPBody:postBody];
149+
150+
FileUploadDelegate* delegate = [[[FileUploadDelegate alloc] init] retain];
151+
delegate.command = self;
152+
delegate.successCallback = successCallback;
153+
delegate.failCallback = failCallback;
154+
delegate.progressCallback = progressCallback;
155+
156+
NSURLConnection* connection = [[NSURLConnection connectionWithRequest:req delegate:delegate] retain];
157+
}
158+
159+
@end
160+
161+
162+
@implementation FileUploadDelegate
163+
164+
@synthesize successCallback, failCallback, progressCallback, responseData, command;
165+
166+
- (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite
167+
{
168+
if(!self.progressCallback) {
169+
return;
170+
}
171+
if (uploadIdx++ % 10 == 0) {
172+
[command writeJavascript: [NSString stringWithFormat:@"%@(%d, %d);", self.progressCallback, totalBytesWritten, totalBytesExpectedToWrite]];
173+
}
174+
}
175+
176+
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
177+
{
178+
NSString* response = [[NSString alloc] initWithData:self.responseData encoding:NSUTF8StringEncoding];
179+
NSLog(@"reponse: %@", response);
180+
NSString* js = [NSString stringWithFormat:@"%@(\"%@\");", self.successCallback, [response stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
181+
[command writeJavascript: js];
182+
[connection autorelease];
183+
[self autorelease];
184+
}
185+
186+
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
187+
{
188+
[command writeJavascript: [NSString stringWithFormat:@"%@(\"%@\");", self.failCallback, [[error localizedDescription] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]];
189+
[connection autorelease];
190+
[self autorelease];
191+
}
192+
193+
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
194+
{
195+
[responseData appendData:data];
196+
}
197+
198+
- (id) init
199+
{
200+
if (self = [super init]) {
201+
self.responseData = [NSMutableData data];
202+
uploadIdx = 0;
203+
}
204+
return self;
205+
}
206+
207+
- (void) dealloc
208+
{
209+
[successCallback release];
210+
[failCallback release];
211+
[responseData release];
212+
[command release];
213+
[super dealloc];
214+
}
215+
216+
217+
@end;

iPhone/FileUploader/README.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# File Uploader plugin for Phonegap - iPhone version #
2+
By Matt Kane
3+
4+
Enables multipart/mime file uploads.
5+
6+
## Adding the Plugin to your project ##
7+
8+
Copy the .h and .m file to the Plugins directory in your project. Copy the .js file to your www directory and reference it from your html file(s).
9+
10+
11+
## Using the plugin ##
12+
The plugin creates the object `window.plugins.fileUploader` with two methods, `upload` and `uploadByUri`.
13+
These are identical except for the format of the reference to the file to upload. `upload` takes an
14+
absolute path, e.g. `/var/tmp/photo_001.jpg`, while `uploadByUri` takes a file:// Uri,
15+
e.g. `file://localhost/var/tmp/photo_001.jpg`.
16+
The full params are as follows:
17+
18+
* server URL of the server that will receive the file
19+
* file Absolute path or uri of the file to upload
20+
* fileKey Parameter name of the file
21+
* params Object with key: value params to send to the server
22+
* fileName Filename to send to the server. Defaults to image.jpg
23+
* mimeType Mimetype of the uploaded file. Defaults to image/jpeg
24+
* success Success callback. Passed the response data from the server as a string.
25+
* fail Error callback. Passed the error message.
26+
* progress Called on upload progress. Signature should be function(bytesUploaded, totalBytes)
27+
28+
This is under development, and the API is likely to change.
29+
30+
31+
## BUGS AND CONTRIBUTIONS ##
32+
The latest bleeding-edge version is available [on GitHub](http://github.com/ascorbic/phonegap-plugins/)
33+
If you have a patch, fork my repo and send me a pull request. Submit bug reports on GitHub, please.
34+
35+
## Licence ##
36+
37+
The MIT License
38+
39+
Copyright (c) 2011 Matt Kane
40+
41+
Permission is hereby granted, free of charge, to any person obtaining a copy
42+
of this software and associated documentation files (the "Software"), to deal
43+
in the Software without restriction, including without limitation the rights
44+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
45+
copies of the Software, and to permit persons to whom the Software is
46+
furnished to do so, subject to the following conditions:
47+
48+
The above copyright notice and this permission notice shall be included in
49+
all copies or substantial portions of the Software.
50+
51+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
52+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
53+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
54+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
55+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
56+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
57+
THE SOFTWARE.
58+
59+
60+
61+
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/**
2+
* Phonegap File Upload plugin
3+
* iPhone version
4+
* Copyright (c) Matt Kane 2011
5+
*
6+
*/
7+
var FileUploader = function() {
8+
9+
}
10+
11+
12+
/**
13+
* Given a file:// url, uploads the file to the server as a multipart/mime request
14+
*
15+
* @param server URL of the server that will receive the file
16+
* @param file file:// uri of the file to upload
17+
* @param fileKey Parameter name of the file
18+
* @param params Object with key: value params to send to the server
19+
* @param fileName Filename to send to the server. Defaults to image.jpg
20+
* @param mimeType Mimetype of the uploaded file. Defaults to image/jpeg
21+
* @param success Success callback. Passed the response data from the server as a string.
22+
* @param fail Error callback. Passed the error message.
23+
* @param progress Called on upload progress. Signature should be function(bytesUploaded, totalBytes)
24+
*/
25+
FileUploader.prototype.uploadByUri = function(server, file, params, fileKey, fileName, mimeType, success, fail, progress) {
26+
this._doUpload('uploadByUri', server, file, params, fileKey, fileName, mimeType, success, fail, progress);
27+
};
28+
29+
/**
30+
* Given absolute path, uploads the file to the server as a multipart/mime request
31+
*
32+
* @param server URL of the server that will receive the file
33+
* @param file Absolute path of the file to upload
34+
* @param fileKey Parameter name of the file
35+
* @param params Object with key: value params to send to the server
36+
* @param fileName Filename to send to the server. Defaults to image.jpg
37+
* @param mimeType Mimetype of the uploaded file. Defaults to image/jpeg
38+
* @param success Success callback. Passed the response data from the server as a string.
39+
* @param fail Error callback. Passed the error message.
40+
* @param progress Called on upload progress. Signature should be function(bytesUploaded, totalBytes)
41+
*/
42+
FileUploader.prototype.upload = function(server, file, params, fileKey, fileName, mimeType, success, fail, progress) {
43+
this._doUpload('upload', server, file, params, fileKey, fileName, mimeType, success, fail, progress);
44+
};
45+
46+
FileUploader.prototype._doUpload = function(method, server, file, params, fileKey, fileName, mimeType, success, fail, progress) {
47+
if (!params) {
48+
params = {}
49+
}
50+
51+
var key = 'f' + this.callbackIdx++;
52+
window.plugins.fileUploader.callbackMap[key] = {
53+
success: function(result) {
54+
success(result);
55+
delete window.plugins.fileUploader.callbackMap[key]
56+
},
57+
fail: function(result) {
58+
fail(result);
59+
delete window.plugins.fileUploader.callbackMap[key]
60+
},
61+
progress: progress
62+
}
63+
var callback = 'window.plugins.fileUploader.callbackMap.' + key;
64+
65+
return PhoneGap.exec('FileUploader.' + method, callback + '.success', callback + '.fail', callback + '.progress', server, file, fileKey, fileName, mimeType, params);
66+
}
67+
68+
FileUploader.prototype.callbackMap = {};
69+
FileUploader.prototype.callbackIdx = 0;
70+
71+
PhoneGap.addConstructor(function() {
72+
if(!window.plugins) {
73+
window.plugins = {};
74+
}
75+
window.plugins.fileUploader = new FileUploader();
76+
});

0 commit comments

Comments
 (0)