iosとnodeダウンロードファイルのアップロード
9210 ワード
まず、アップロードとダウンロードにはserverとclientが協力する必要があります.同じクライアントコードはservletで成功するかもしれないし、nodeに変えるのはだめだし、逆にサービス側によってhttpリクエストの処理が異なる可能性があるからだ.本論文では,サービス側がnodeを用い,クライアントがNSURLSessionを用いる場合について述べる.
サービス側コード
node+expressよりも簡単な実装方法はまだ見たことがありません.
上はサービス側のすべてのコードです.deferプロパティをtrueに設定すると、次の2つのライフサイクルコールバックが有効になります.しかし、このサービスは、直接CocoaRestClientでPOSTリクエストを送信しても通じず、httpヘッダにContent-Typeを追加する必要があるようです.
アップロードされたクライアントコード
Viewは省略し、重要なViewControlコードのみを紹介します
主にNSURLSessionTaskDelegateプロトコルを実装します.進捗バーを実装するには、ライフサイクルメソッドが必要です.
初期化コードは次のとおりです.
ここでは、いくつかのインスタンス変数を初期化します.次に、最も重要な方法を示します.
NSURLSessionUploadTaskをどうやって手に入れるかがポイントです.NSURLSessionはuploadTaskWithRequest:fromFile:方法を提供していますが、実践を経て、走っていないことがわかりました.NSURLSessionはContent-Typeヘッダを自動的に追加したり、Dataにboundaryを自動的に追加したりしないようです.結果としてserver側がエラーを報告します.
TypeError: Cannot call method 'on' of undefined at/Users/apple/WebstormProjects/uploadAndDownloadServer/lib/main.js:15:14 at callbacks (/Users/apple/WebstormProjects/uploadAndDownloadServer/node_modules/express/lib/router/index.js:161:37) at param (/Users/apple/WebstormProjects/uploadAndDownloadServer/node_modules/express/lib/router/index.js:135:11) at pass (/Users/apple/WebstormProjects/uploadAndDownloadServer/node_modules/express/lib/router/index.js:142:5) at Router._dispatch (/Users/apple/WebstormProjects/uploadAndDownloadServer/node_modules/express/lib/router/index.js:170:5) at Object.router (/Users/apple/WebstormProjects/uploadAndDownloadServer/node_modules/express/lib/router/index.js:33:10) at next (/Users/apple/WebstormProjects/uploadAndDownloadServer/node_modules/express/node_modules/connect/lib/proto.js:190:15) at next (/Users/apple/WebstormProjects/uploadAndDownloadServer/node_modules/express/node_modules/connect/lib/proto.js:165:78) at multipart (/Users/apple/WebstormProjects/uploadAndDownloadServer/node_modules/express/node_modules/connect/lib/middleware/multipart.js:60:27) at/Users/apple/WebstormProjects/uploadAndDownloadServer/node_modules/express/node_modules/connect/lib/middleware/bodyParser.js:57:9
だから私の最後のやり方は、自分でFileからDataを読み出し、必要なコントロールをつづることです.これはprepareDataForUpload()メソッドで実現されています.
最後にDelegate methodメソッドです.
これは簡単です.あまり紹介しません.totalBytesSentとtotalBytesExpectedSendの2つの変数があります.テキストヒントを作るにしても、進捗バーを作るにしても、簡単に実現できます.
しかし、上記のサンプルコードは、自分をdelegateに設定しやすいようにしています.実際のプロジェクトでは、ビジネスロジックのクラスをuploadコンポーネントのdelegateに設定する必要があります.アップロード後に何をすべきかは、ビジネスコンポーネントで制御すべきだからです.
ダウンロードされたクライアントコード
アップロードされたコードよりもダウンロードが簡単です.
コードは、block callback付きの別のAPIではなくdownloadTaskWithRequest:メソッドを呼び出すことに注意してください.completionHandlerが設定されている場合、delegate methodは呼び出されませんが、アップロードと同様にdelegate methodがダウンロードの進捗バーを実現する必要があることがわかりました.
この方法では、進捗バーを実装できます.
ダウンロードしたファイルは、tmpディレクトリの下に置いてあり、処理しないとすぐに削除されるので、別のdelegate methodで最終パスにコピーする必要があります.
サービス側コード
node+expressよりも簡単な実装方法はまだ見たことがありません.
var express = require("express");
var app = express();
app.use(express.bodyParser({
uploadDir: __dirname + '/../var/uploads',
keepExtensions: true,
limit: 100 * 1024 * 1024,
defer: true
}))
.use('/svc/public', express.static(__dirname + '/../public'));
app.post('/svc/upload', function (req, res) {
req.form.on('progress', function (bytesReceived, bytesExpected) {
});
req.form.on('end', function () {
var tmp_path = req.files.file.path;
var name = req.files.file.name;
console.log("tmp_path: "+ tmp_path);
console.log("name: "+name);
res.end("success");
});
});
app.listen(3000);
console.log("server started at 3000 port");
上はサービス側のすべてのコードです.deferプロパティをtrueに設定すると、次の2つのライフサイクルコールバックが有効になります.しかし、このサービスは、直接CocoaRestClientでPOSTリクエストを送信しても通じず、httpヘッダにContent-Typeを追加する必要があるようです.
アップロードされたクライアントコード
Viewは省略し、重要なViewControlコードのみを紹介します
@interface YLSUploadViewController : UIViewController<NSURLSessionTaskDelegate>
-(void) doUpload;
@end
主にNSURLSessionTaskDelegateプロトコルを実装します.進捗バーを実装するには、ライフサイクルメソッドが必要です.
初期化コードは次のとおりです.
{
NSString *boundary;
NSString *fileParam;
NSURL *uploadURL;
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
boundary = @"----------V2ymHFg03ehbqgZCaKO6jy";
fileParam = @"file";
uploadURL = [NSURL URLWithString:@"http://192.168.1.103:3000/svc/upload"];
}
return self;
}
ここでは、いくつかのインスタンス変数を初期化します.次に、最も重要な方法を示します.
-(void) doUpload
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];
NSData *body = [self prepareDataForUpload];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:uploadURL];
[request setHTTPMethod:@"POST"];
// 2 ,NSURLSessionUploadTask Content-Type
NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary];
[request setValue:contentType forHTTPHeaderField: @"Content-Type"];
NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromData:body completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){
NSString *message = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"message: %@", message);
[session invalidateAndCancel];
}];
[uploadTask resume];
});
}
-(NSData*) prepareDataForUpload
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *uploadFilePath = [documentsDirectory stringByAppendingPathComponent:@"QQ.dmg"];
NSString *fileName = [uploadFilePath lastPathComponent];
NSMutableData *body = [NSMutableData data];
NSData *dataOfFile = [[NSData alloc] initWithContentsOfFile:uploadFilePath];
if (dataOfFile) {
[body appendData:[[NSString stringWithFormat:@"--%@\r
", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r
", fileParam, fileName] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[@"Content-Type: application/zip\r
\r
" dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:dataOfFile];
[body appendData:[[NSString stringWithFormat:@"\r
"] dataUsingEncoding:NSUTF8StringEncoding]];
}
[body appendData:[[NSString stringWithFormat:@"--%@--\r
", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
return body;
}
NSURLSessionUploadTaskをどうやって手に入れるかがポイントです.NSURLSessionはuploadTaskWithRequest:fromFile:方法を提供していますが、実践を経て、走っていないことがわかりました.NSURLSessionはContent-Typeヘッダを自動的に追加したり、Dataにboundaryを自動的に追加したりしないようです.結果としてserver側がエラーを報告します.
TypeError: Cannot call method 'on' of undefined at/Users/apple/WebstormProjects/uploadAndDownloadServer/lib/main.js:15:14 at callbacks (/Users/apple/WebstormProjects/uploadAndDownloadServer/node_modules/express/lib/router/index.js:161:37) at param (/Users/apple/WebstormProjects/uploadAndDownloadServer/node_modules/express/lib/router/index.js:135:11) at pass (/Users/apple/WebstormProjects/uploadAndDownloadServer/node_modules/express/lib/router/index.js:142:5) at Router._dispatch (/Users/apple/WebstormProjects/uploadAndDownloadServer/node_modules/express/lib/router/index.js:170:5) at Object.router (/Users/apple/WebstormProjects/uploadAndDownloadServer/node_modules/express/lib/router/index.js:33:10) at next (/Users/apple/WebstormProjects/uploadAndDownloadServer/node_modules/express/node_modules/connect/lib/proto.js:190:15) at next (/Users/apple/WebstormProjects/uploadAndDownloadServer/node_modules/express/node_modules/connect/lib/proto.js:165:78) at multipart (/Users/apple/WebstormProjects/uploadAndDownloadServer/node_modules/express/node_modules/connect/lib/middleware/multipart.js:60:27) at/Users/apple/WebstormProjects/uploadAndDownloadServer/node_modules/express/node_modules/connect/lib/middleware/bodyParser.js:57:9
だから私の最後のやり方は、自分でFileからDataを読み出し、必要なコントロールをつづることです.これはprepareDataForUpload()メソッドで実現されています.
最後にDelegate methodメソッドです.
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
これは簡単です.あまり紹介しません.totalBytesSentとtotalBytesExpectedSendの2つの変数があります.テキストヒントを作るにしても、進捗バーを作るにしても、簡単に実現できます.
しかし、上記のサンプルコードは、自分をdelegateに設定しやすいようにしています.実際のプロジェクトでは、ビジネスロジックのクラスをuploadコンポーネントのdelegateに設定する必要があります.アップロード後に何をすべきかは、ビジネスコンポーネントで制御すべきだからです.
ダウンロードされたクライアントコード
アップロードされたコードよりもダウンロードが簡単です.
-(void) doDownload
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];
NSURL *url = [NSURL URLWithString:@"http://192.168.1.103:3000/svc/public/bigfile.dmg"];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
[request setHTTPMethod:@"GET"];
NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request];// block
[downloadTask resume];
});
}
コードは、block callback付きの別のAPIではなくdownloadTaskWithRequest:メソッドを呼び出すことに注意してください.completionHandlerが設定されている場合、delegate methodは呼び出されませんが、アップロードと同様にdelegate methodがダウンロードの進捗バーを実現する必要があることがわかりました.
@interface YLSDownloadViewController : UIViewController<NSURLSessionDownloadDelegate>
この方法では、進捗バーを実装できます.
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
ダウンロードしたファイルは、tmpディレクトリの下に置いてあり、処理しないとすぐに削除されるので、別のdelegate methodで最終パスにコピーする必要があります.
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *distFilePath = [documentsDirectory stringByAppendingPathComponent:@"success.dmg"];
NSString* tempFilePath = [location path];
NSFileManager *fileManager = [NSFileManager defaultManager];
if([fileManager fileExistsAtPath:tempFilePath]){
[fileManager copyItemAtPath:tempFilePath toPath:distFilePath error:nil];
}
[session invalidateAndCancel];
}