一、基本需求:主要是实现ios客户端可以访问linux服务器上面配置的fastdfs服务器,能够实现上传和下载文件
二、基本信息:
最新版本代码地址:https://github.com/happyfish100/
fastdfs版本:FastDFS_v5.08.tar.gz,libfastcommon也是最新版本,下载版本时间大约2018.03.01左右
三、主要思路:
1.ios如果选择用object-c开发客户端的话(现在应该有其它方式来开发,未研究),可以调用C语言程序,具体可百度,网上很多demo,需要增加对应的.h文件(https://blog.csdn.net/hitourlee/article/details/38511435)
2.主要实现ios客户端能访问服务器,进行文件的上传和下载操作。需要先在Mac上编译fastdfs(不需要安装,将make.sh文件里面引用libfastcommon的地方换成解压后的目录),目前作者提供的版本早已支持在Mac上编译,经过修改make.sh,添加了宏定义等,在Mac上面能编译通过,也能够正常进行下载和上传文件。
client目录添加make.sh文件,通过脚本去编译,添加的宏定义如下,(脚本其它部分未贴出来,可参考代码里面本身的脚本):
但是编译出来的静态包libfdfsclient.a文件,默认是x86_64的,iPhone5以后的cpu架构是需要静态包支持arm64架构的,我未去查怎么修改脚本文件编译出来arm64架构的静态包,而是直接在xcode上将代码导入,修改xcode上的配置(这里可以百度搜一下编译出来arm64静态包,都有资料)
xcode里面编译静态库的时候,可以设置宏定义,这样才能满足作者设置的条件编译
3.这里对余大侠的代码,稍微修改了下,将main函数里面的控制台参数传入功能封装到函数里面,并配套写对应的.h文件
fdfs_upload_download.h,内容如下:
/*
config_file: client.conf
server_file: like group1/M00/00/83/CgAACFqfm6mAZBvIAAAUABzabag5036295
local_filename: your save file name in local, include file path下载文件将ios客户端要保存的路径包括文件名传入了,ios客户端的文件是沙盒形式,需要客户端自己设置自己的目录(如cache目录),才有权限去写
*/
int download_file(char *conf_filename, char *server_file, char *local_filename);
/*
config_file: client.conf
local_filename: like /home/wb/1.jpg
file_id: return server value, like group1/M00/00/83/CgAACFqfm6mAZBvIAAAUABzabag5036295上传文件成功,直接返回服务器的相对路径
*/
char * upload_file(char *conf_filename, char *local_filename);
这样修改后,能够成功编译出来,ios客户端能够成功下载,但是在文件上传时,可能会遇到问题;
4.遇到的主要问题是C语言在ios上的系统调用问题
在xcode上将编译出来的libfdfsclient.a文件,将其和修改后的上传文件和下载文件的接口(.h头文件)放到xcode里面编译运行,上传文件时报错:
sendfile系统调用返回-1,加日志具体errno返回78,打印出function not implemented。
看了下作者在这里有处理,区分了linux系统和FreeBSD系统(darwin),但这里在Mac Pro苹果笔记本上运行没有问题,ios手机上有问题。
难道是ios系统将这个系统调用的实现砍掉了?即使砍掉了,为什么ios11.2的sdk里面socket.h这个头文件里面还有sendfile这个方法的声明,如果砍掉就将声明一起去掉好了,这点令人费解,
解决方法:我利用的最原始发送文件的方法,未去查看其它更好的方法,不用优化后的sendfile函数,如果有更好方法的同学,可以指点下,或者余大侠修改下,提供下技术支持
加了一个宏定义,如果是ios平台,走此条件,其它的依旧走sendfile流程
修改文件:libfastcommon--->src--->sockopt.c--->tcpsendfile_ex函数
#ifdef OS_FREEBSD
offset = file_offset;
#if defined(DARWIN)
# if defined(IOS) //增加代码开始处
int BUFFER_SIZE = 1024;
char buffer[BUFFER_SIZE];
result = 0;
FILE *fp = fopen(filename, "r");
if(NULL == fp)
{
printf("File:%s Not Found\n", filename);
exit(1);
}
else
{
bzero(buffer, BUFFER_SIZE);
int length = 0;
while((length = fread(buffer, sizeof(char), BUFFER_SIZE, fp)) > 0)
{
//printf("length:%d\n", length);
if(send(sock, buffer, length, 0) < 0)
{
printf("Send File:%s Failed\n", filename);
break;
}
bzero(buffer, BUFFER_SIZE);
}
fclose(fp);
} //增加代码结束处
#else
result = 0;
remain_bytes = file_bytes;
int sendres = -1;
while (remain_bytes > 0)
{
off_t len;
len = remain_bytes;
sendres = sendfile(fd, sock, offset, &len, NULL, 0);
printf("sendfile result sendres:%d\n", sendres);
if (sendres != 0) {
result = errno != 0 ? errno : EIO;
printf("result:%d\n", result);
if (result != EINTR)
{
break;
}
}
remain_bytes -= len;
}
#endif
#else
remain_bytes = file_bytes;
result = 0;
while (remain_bytes > 0)
{
off_t sbytes;
sbytes = 0;
if (sendfile(fd, sock, offset, remain_bytes, NULL, &sbytes, 0) != 0)
{
result = errno != 0 ? errno : EIO;
if (result != EINTR)
{
break;
}
}
remain_bytes -= sbytes;
}
#endif
#endif
至此,编译出来的静态库文件能够被ios客户端正常使用,需要写一个ios demo来测试一下,下面是新建ios demo里面修改的代码,将.a文件和.h文件导入工程
//
// ViewController.m
// test0321_02
//
// Created by waj on 2018/3/21.
// Copyright © 2018年 waj. All rights reserved.
//
#import "ViewController.h"
#import "fdfs_up_download_file.h"
#import <fcntl.h>
#import <errno.h>
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//首先寻找cache里面是否有对应图片
//获取Caches目录
NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
NSLog(@"cachePath:%@\n", cachesPath);
NSString *filePath = [cachesPath stringByAppendingString:[NSString stringWithFormat:@"/%@",@"1.png"]];
NSLog(@"filePath:%@\n", filePath);
// NSString *server_file = @"group1/M00/00/00/rBADzVpNxKuABlV1AAAimOO322c001.png";
// NSLog(@"server_file:%@\n", server_file);
NSString *configFilePath = [self getConfigFilePath];
NSLog(@"configFilePath:%@\n", configFilePath);
//下载文件测试
// int test = download_file([configFilePath UTF8String], [server_file UTF8String], [filePath UTF8String]);
// NSLog(@"test:%d",test);
UIImage *image = [UIImage imageWithContentsOfFile:filePath];
NSLog(@"==");
dispatch_async(dispatch_get_global_queue(0, 0), ^{
//处理耗时操作
char *serverUrl = upload_file([configFilePath UTF8String], [filePath UTF8String]);
NSLog(@"upload_result:%s", serverUrl);
//通知主线程更新
dispatch_async(dispatch_get_main_queue(), ^{
//回调或者通知主线程更新
});
});
}
4.测试过程中,还有一些问题,还没来的及分析,在某些android手机、ios手机上,fastdfs会报出no route to host的错误,网上大多都说是服务器的防火墙打开了,我这里确定不是这个防火墙的问题,如果是防火墙的问题,所有的客户端访问都会有问题,而不是某些手机有问题。ios手机版本10.3.2和11.2.x的 版本iphone6和iPhone6plus上是正常的,但是一位同事的ios版本11.2.x出现该问题
2018-03-29 10:54:41.879915+0800 test0329[21422:8431941] cachePath:/var/mobile/Containers/Data/Application/8F3B9EFE-C7C9-4554-8356-F8CDEA8FD368/Library/Caches
[2018-03-29 10:54:41] ERROR - file: /Users/waj/Desktop/Fastdfs-common/libfastcommon-master/src/connection_pool.c, line: 130, connect to 10.0.3.14:22122 fail, errno: 65, error info: No route to host
待进一步去分析,看能不能从某些手机无法建立socket连接去分析,写一个demo测试排除下问题
另:可能已经有兄弟早将ios版本编译出来,成功运行过,也请指点下。看有无更好的方法。
余大侠发的帖子里面有qq群,本来想请教下,然而加不上,应该是人员早满了,所以就自己尝试了
参考:http://bbs.chinaunix.net/forum.php?mod=forumdisplay&fid=240&typeid=428
http://bbs.chinaunix.net/thread-1920470-1-1.html