iOS文件上传功能

iOS文件上传功能

									Morris_
									2019.05.14

demo下载

搭建本地php环境

在本地搭建一个php服务,在iOS端进行图片、文件等上传测试。

一、搭建服务

常用命令

查看本地php版本
php - v

在本机上开一个服务,并且指定工作目录。局域网内用户都可访问。
php -S 0.0.0.0:9999 -t /Users/Document/php/

监听该服务
Listening on http://0.0.0.0:9999

开启服务
php -S 0.0.0.0:9999 -t /Users/Document/php/

//关闭服务
Ctrl + c

搭建一个本地的php服务

1、搭建php环境

php - v 查看php环境版本,我是下载了个软件,在电脑上默认装了php环境,具体怎么装还得看专业的。

2、本地新建一个php工作文件夹

我在/Users/xxx/Documents/php/下创建了文件夹upload,则工作空间是在/Users/xxx/Documents/php/upload/下。

3、在upload文件夹下创建一个文件,改名字为upload_file.php保存

即创建一个php文件,在文件中写如下php代码

<?php

ini_set('memory_limit',0);
ini_set('post_max_size','1024G');
ini_set('upload_max_filesize','1000G');
set_time_limit(0);

move_uploaded_file($_FILES["file"]["tmp_name"],"/Users/xxx/Documents/php/upload/".$_FILES["file"]["name"]);
exit(json_encode([
    "status"=>200
]));

设置上传文件大小限制。

move_uploaded_file()这个方法是下载文件,并将文件move操作。

上传上来的文件是uploaded,会到电脑的/tmp目录下,move操作将其转移到upload目录下。上传的文件首先是会短暂的存储到电脑的/tmp文件中,如果不做处理,生命周期结束后,/tmp中文件将被删除。

上传成功后有个返回码200。

4、开启服务

终端中进入工作目录upload文件下:cd /Users/xxx/Documents/php/upload

开启服务并指定当前服务的工作区间:php -S 0.0.0.0:9999 -t /Users/xxx/Documents/php/upload/

完成

然后就OK了,局域网内的用户都可访问,域名是本地ip+/upload.php: http://172.16.11.223:9999/upload.php

二、上传文件

选取手机相册里的图片、拍照、选取iCloud里面的文件,进行上传。

基础知识

先看一些基础知识。

  • 打开相册

先在plist文件中添加相册使用权限申请的key和value。

- (IBAction)photoABtnClick:(UIButton *)sender {
    UIImagePickerController *picker = [[UIImagePickerController alloc] init];
    //如果需要编辑图片,建议sourceType选择UIImagePickerControllerSourceTypeSavedPhotosAlbum,如果不需要,可选择UIImagePickerControllerSourceTypePhotoLibrary。
    picker.allowsEditing = YES;
    picker.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
    picker.delegate = self;
    [self presentViewController:picker animated:YES completion:nil];
}
  • 打开相机

先在plist文件中添加相机使用权限申请的key和value。

- (IBAction)cameraBtnClick:(UIButton *)sender {
    UIImagePickerController *picker = [[UIImagePickerController alloc] init];
    picker.allowsEditing = YES;
    picker.sourceType = UIImagePickerControllerSourceTypeCamera;
    picker.delegate = self;
    [self presentViewController:picker animated:YES completion:nil];
}
  • 打开iCloud
- (IBAction)iCloudBtnClick:(UIButton *)sender {
    if (![DocUploadViewController ICloudEnable]) {
        NSLog(@"iCloud没有开启");
        return;
    }
    NSArray *documentTypes = @[@"public.content",
                               @"public.text",
                               @"public.source-code",
                               @"public.image",
                               @"public.jpeg",
                               @"public.png",
                               @"com.adobe.pdf",
                               @"com.apple.keynote.key",
                               @"com.microsoft.word.doc",
                               @"com.microsoft.excel.xls",
                               @"com.microsoft.powerpoint.ppt"];
    UIDocumentPickerViewController *picker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:documentTypes inMode:UIDocumentPickerModeOpen];
    picker.delegate = self;
    [self presentViewController:picker animated:YES completion:nil];
}

使用iCloud前先判断是否有权限:

+ (BOOL)ICloudEnable {
    NSURL *url = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
    return url != nil;
}

UIImagePickerController和UIDocumentPickerViewController是系统的UIKit框架下的两个类。在使用iCloud前需要先打开App的iCloud使用权限,请阅读 Enabling CloudKit in Your App

  • 压缩图片的方法
UIImageJPEGRepresentation(image, 0.8)
  • 获取相册回调返回的图片URL
 NSURL *url = nil;
 if (@available(iOS 11.0, *)) {
     url = info[UIImagePickerControllerImageURL];
 } else {
     url = info[UIImagePickerControllerReferenceURL];
 }
  • 获取选择相册/拍照的照片
 //获取经过编辑后的图片
 UIImage *image = info[UIImagePickerControllerEditedImage];
 if (!image) {
 //如果未编辑,取原图
 image = info[UIImagePickerControllerOriginalImage];
 }
  • 将图片保存到相册
 //保存到相册
 [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
    [PHAssetChangeRequest creationRequestForAssetFromImage:iamge];
 } completionHandler:^(BOOL success, NSError * _Nullable error) {
 
 }];

PHPhotoLibrary是系统的Photos框架下的一个类,使用需引头文件#import <Photos/Photos.h>。

performChanges相当于打开相册,PHAssetChangeRequest *request = [PHAssetChangeRequest creationRequestForAssetFromImage:image];是将图片写入相册,通过request.placeholderForCreatedAsset.localIdentifier可读取写入后的文件id。

  • 获取相册图片信息
 //获取图片信息
 PHFetchResult *result = [PHAsset fetchAssetsWithLocalIdentifiers:@[locolId] options:nil];
 PHAsset *asset = [result firstObject];
 [[PHImageManager defaultManager] requestImageDataForAsset:asset options:nil resultHandler:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) {
 
    NSLog(@"==>%@",info);
 
 }];

这里的locolId就是保存在相册里的文件identifier。

  • 读取iCoud文件信息
	 //创建doc
    WeCDocument *document = [[WeCDocument alloc] initWithFileURL:documentsURL];
    
    //打开doc
    [document openWithCompletionHandler:^(BOOL success) {
        if (success)
        {
        
        }
        else
        {
        
        }
        //关闭doc
        [document closeWithCompletionHandler:^(BOOL success) {
            
        }];
    }];

由于CompletionHandler这个block里对document持有一份引用,多以document对象不会被直接释放掉,等回调过来后需要关闭doc,document对象才会被释放。

这里ICDocument是继承UIDocument的类,实现如下:

@interface WeCDocument : UIDocument

@property (nonatomic, strong, nullable) NSData *data;
//文件名
@property (nonatomic, copy, nullable) NSString *fileName;
//文件类型
@property (nonatomic, copy, nullable) NSString *MIMEType;

@end

@implementation WeCDocument

- (BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName error:(NSError * _Nullable __autoreleasing *)outError
{
    //.page等文件会闪退
    if ([contents isKindOfClass:[NSData class]])
    {
        self.data = [contents copy];
        self.fileName = self.fileURL.lastPathComponent;
        if (self.fileName && self.fileName.length) {
            NSRange startRange = [self.fileName rangeOfString:@"."];
            self.MIMEType = [self.fileName substringFromIndex:startRange.location];
        }
    }
    else {
        NSLog(@"读取文件出错!");
        return NO;
    }
    return YES;
}

@end

这里通过documentURL去获取的文件名和类型。

信息回调

  • UIImagePickerControllerDelegate

选择相册文件或者拍照后,回调方法:

//selected
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
    //此处需要先dismiss掉picker,然后再present出alert,佛否则alert显示不出,bug
    [picker dismissViewControllerAnimated:YES completion:nil];
    
    //获取经过编辑后的图片
    UIImage *image = info[UIImagePickerControllerEditedImage];
    if (!image) {
        //如果未编辑,取原图
        image = info[UIImagePickerControllerOriginalImage];
    }
    
    if (picker.sourceType == UIImagePickerControllerSourceTypeSavedPhotosAlbum)
    {
        NSURL *url = nil;
        if (@available(iOS 11.0, *)) {
            url = info[UIImagePickerControllerImageURL];
        } else {
            url = info[UIImagePickerControllerReferenceURL];
        }
        [self selectedImage:image url:url reName:nil];
    }
    else if (picker.sourceType == UIImagePickerControllerSourceTypeCamera)
    {
        __block NSString *locolId = nil;
        
        [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
            //保存到相册
            PHAssetChangeRequest *request = [PHAssetChangeRequest creationRequestForAssetFromImage:image];
            locolId = request.placeholderForCreatedAsset.localIdentifier;
        } completionHandler:^(BOOL success, NSError * _Nullable error) {
            if (error == nil) {
                //获取图片信息
                PHFetchResult *result = [PHAsset fetchAssetsWithLocalIdentifiers:@[locolId] options:nil];
                PHAsset *asset = [result firstObject];
                [[PHImageManager defaultManager] requestImageDataForAsset:asset options:nil resultHandler:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) {
                    
                    NSURL *url = info[@"PHImageFileURLKey"];
                    [self selectedImage:image url:url reName:nil];
                }];
            }
            else {
                NSLog(@"图片保存失败!");
            }
        }];
    }
}
//cancel
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
    [picker dismissViewControllerAnimated:YES completion:nil];
}
  • UIDocumentPickerDelegate
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray <NSURL *>*)urls
{
    [self selectedDocumentAtURLs:urls reName:nil];
}
- (void)documentPickerWasCancelled:(UIDocumentPickerViewController *)controller
{
    [controller dismissViewControllerAnimated:YES completion:nil];
}
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentAtURL:(NSURL *)url
{
    [self selectedDocumentAtURLs:@[url] reName:nil];
}

iCloud选取文件后,会返回一个documentURL,即文件路径的URL。

上传

起初我想都通过传入url,读取文件,然后上传,后来发现了问题:NSURL有个属性isFileURL,iCloud获取的文件返回的documentURL的isFileURL是YES,这说明从iCloud获取的documentURL是文件URL。但是从相册或者拍照得到的文件的url并非是文件路径,它的isFileURL是NO。从documentURL里获取文件信息可以通过系统提供的类UIDocument来获取,但是相册和拍照的url不是文件url,使用UIDocument直接闪退,闪退信息:*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'must pass a valid file URL to -[UIDocument initWithFileURL:]'

看来获取文件信息,相册/拍照和iCloud是不同的。

后来分别提供了两个方法,一个上传图片,一个上传iCloud文件。

/**
 *  iCloud文件上传
 *  documentsURL 文件地址;rename 文件重命名,文件名不需带文件类型后缀
 */
+ (void)uploadICloudFileWithUrl:(NSURL *)documentsURL
                         reName:(nullable NSString *)rename
                       progress:(nullable void (^)(NSProgress * progress))uploadProgress
                         sucess:(void(^)(NSDictionary *responseObject))uploadSuccess
                        failure:(void(^)(NSError *error))uploadFailure;

/**
 图片上传,相册、拍照文件上传。
 url为referenceURL,相册UIImagePickerControllerReferenceURL,拍照PHImageFileURLKey,可为空,为空时不会上传源文件名称。
 rename,通过此值修改上传文件名称,文件名不需带文件类型后缀。
 */
+ (void)uploadImage:(UIImage *)image
       referenceURL:(nullable NSURL *)url
             reName:(nullable NSString *)rename
           progress:(nullable void (^)(NSProgress * progress))uploadProgress
             sucess:(void(^)(NSDictionary *responseObject))uploadSuccess
            failure:(void(^)(NSError *error))uploadFailure;

这块代码比较多,直接看demo吧,调试demo前先自己搭建好本地的php服务,修改请求地址,然后可以上传图片到本地服务。

疑问

  • 网上有关获取文件的MIMEType方法
-(NSString *)getMIMETypeURLRequestAtPath:(NSURL *)path
{
    NSURL *url = path;//[NSURL fileURLWithPath:path];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    NSHTTPURLResponse *response = nil;
    [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
    NSString *mimeType = response.MIMEType;
    return mimeType;
}

我很纳闷,这个是个异步的怎么去获取MIMEType,难道是等获取到MIMEType在做其他操作吗?

人云亦云?或者说我不知道怎么使用?

©️2020 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值