iOS開發之NSURLSessionUploadTask上傳數據

ChristyDalg 7年前發布 | 11K 次閱讀 iOS開發 移動開發

蘋果在 iOS9 之后已經廢棄了 NSURLConnection , NSURLSession 成為其替代者,其基本知識網上很多,主要可以從 NSURLSessionDataTask 、 NSURLSessionDownloadTask 和 NSURLSessionUploadTask 入手學習。最近在寫案例時發現其中的 NSURLSessionUploadTask 還是有著不少的坑,在開發時有時候很難一次性成功。所以將研究的過程記錄與分享一下。我會以一個完整的案例來講解如何使用。

服務器開發

環境:IDEA 14 +Tomcat 8.x+JDK 8

1、編寫服務器端代碼

由于上傳數據與下載數據不同,下載的時候只要把數據丟進服務器就可以了。但是上傳需要服務器自己來處理。所以以 Java Servlet 來寫服務器端,由于Servlet 3.0 以后可以直接處理文件上傳,所以相對比較簡單,代碼如下,注釋很詳細。

import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;

@MultipartConfig //標識Servlet支持文件上傳
public class UploadServlet extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        //存儲路徑為我們的根目錄
        String storePath = req.getServletContext().getRealPath("/");
        //獲取part對象,參數為客戶端表單中的name屬性的值
        Part part = req.getPart("myfile");
        //Servlet3沒有提供直接獲取文件名的方法,需要從請求頭中解析出來
        //獲取請求頭
        String header = part.getHeader("Content-Disposition");
        //獲取文件名
        String fileName = header.substring(header.lastIndexOf("=") + 2, header.length() - 1);
        //把文件寫到指定路徑
        part.write(storePath + File.separator + fileName);
        //回寫數據給客戶端
        resp.setCharacterEncoding("UTF-8");
        PrintWriter pw = resp.getWriter();
        pw.print("上傳成功");
    }

}

2、部署代碼

找到Tomcat根目錄下的conf文件夾,打開server.xml,在最后加上一行代碼,path就是訪問的項目路徑,docBase就是項目編譯后的位置。

<Context path="/AppTestAPI" docBase="E:\AppTestAPI\out\artifacts\AppTestAPI" auth="Container" />

3、啟動服務器

通過瀏覽器訪問 http://localhost 能出現如下的界面,至此完成服務器端工作。( 由于我修改了默認端口,所以沒有加8080 )

Paste_Image.png

客戶端開發

環境:Xcode 7.3.1

1、創建項目

創建一個項目,布置界面,設置支持http網絡訪問。主界面如下:

界面.png

2、編寫代碼

NSURLSession使用都是一個套路:創建請求,創建任務,執行任務,成功回調。但是在使用NSURLSessionUploadTask進行上傳時最麻煩的是上傳數據的構造,其遵循嚴格的規范,如下圖,不能隨意書寫,不能隨意書寫,不能隨意書寫~,重要的事情說三遍,否則坑得你生活不能自理。

上傳必填字段.png

下面是ViewController的代碼,其中最核心的是 getData 方法。

#import "ViewController.h"

 //分隔符
#define YFBoundary @"AnHuiWuHuYungFan"
//換行
#define YFEnter [@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]
//NSString轉NSData
#define YFEncode(string) [string dataUsingEncoding:NSUTF8StringEncoding]

@interface ViewController ()<NSURLSessionTaskDelegate>

@property (weak, nonatomic) IBOutlet UIProgressView *uploadProgress;
@property (weak, nonatomic) IBOutlet UILabel *uploadInfo;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    //1、確定URL
    NSURL *url = [NSURL URLWithString:@"http://192.168.0.5/AppTestAPI/UploadServlet"];    
    //2、確定請求
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];  
    //3、設置請求頭
    NSString *head = [NSString stringWithFormat:@"multipart/form-data;boundary=%--@", YFBoundary];
    [request setValue:head forHTTPHeaderField:@"Content-Type"];
    //4、設置請求方式,上傳時必須是Post請求
    request.HTTPMethod = @"POST";
    //5、創建NSURLSession
    NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    //6、獲取上傳的數據
    NSData *uploadData = [self getData];
    //7、創建上傳任務 上傳的數據來自getData方法
    NSURLSessionUploadTask *task = [session uploadTaskWithRequest:request fromData:uploadData completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {

       //上傳成功以后改變UILabel文本為服務器返回的數據
       self.uploadInfo.text =[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];

    }];
    //8、執行上傳任務
    [task resume];

}


/**
 *  設置請求體
 *
 *  @return 請求體內容
 */
-(NSData *)getData
{

    NSMutableData *data = [NSMutableData data];

    //1、開始標記
    //--
    [data appendData:YFEncode(@"--")];
    //boundary
    [data appendData:YFEncode(YFBoundary)];
    //換行符
    [data appendData:YFEnter];
    //文件參數名 Content-Disposition: form-data; name="myfile"; filename="wall.jpg"
    [data appendData:YFEncode(@"Content-Disposition:form-data; name=\"myfile\"; filename=\"wall.jpg\"")];
    //換行符
    [data appendData:YFEnter];
    //Content-Type 上傳文件的類型 MIME
    [data appendData:YFEncode(@"Content-Type:image/jpeg")];
    //換行符
    [data appendData:YFEnter];
    //換行符
    [data appendData:YFEnter];
    //2、上傳的文件數據

    //圖片數據  并且轉換為Data
    UIImage *image = [UIImage imageNamed:@"wall.jpg"];
    NSData *imagedata = UIImageJPEGRepresentation(image, 1.0);
    [data appendData:imagedata];
    //換行符
    [data appendData:YFEnter];

    //3、結束標記
    //--
    [data appendData:YFEncode(@"--")];
    //boundary
    [data appendData:YFEncode(YFBoundary)];
    //--
    [data appendData:YFEncode(@"--")];
    //換行符
    [data appendData:YFEnter];

    return data;

}

#pragma mark - 代理方法 只要給服務器上傳數據就會調用 可以計算出上傳進度
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
{

    //設置進度條
    self.uploadProgress.progress = 1.0 * totalBytesSent / totalBytesExpectedToSend;
}

@end

最終效果

先看客戶端的表象

客戶端演示.gif

再看服務器端最終上傳的數據,不重要以為是圖片,靜靜觀察一會兒~~~有變化

服務器端.gif

 

來自:http://www.jianshu.com/p/f0a9c47167fd

 

 本文由用戶 ChristyDalg 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!