附上项目地址CloudDisk;我已经在里面把需要的安装包都打包好了,一键安装脚本只完成了一半,慎用,不过很快就会完善它。之前是用c来完成功能的,现在想重新用C++重新架构一下,方便后续在上面增加功能。
我还是个刚入门萌新,分享的一些东西并不一定准确,还望大佬们多多指教。
一、代码架构
二、代码解析
代码文件github上面有,就不详细列出来了,简单说一下业务逻辑。
file.h头文件,主要作为文件属性类,其中包含文件上传操作时所需要获取的所有文件属性,方便后续各种操作。upload.h继承file.h,其中封装了fastCGI的接收文件函数FCGI_Accept();然后定义了解析数据函数ParseDataAndSave(),实现对通过cgi传输的数据的解析功能。其中通过对数据的截取,得到了文件名,并且通过memcmp()函数拷贝了一份文件,这份拷贝的文件就是后面写入fastDFS的文件。UploadFile()函数实现了对fastDFS文件上传流程的封装,具体代码我贴在下面:
bool Upload::UploadFile(char *fileName) {
if (!fastDFS.FdfsClientInit()) {
cout << "fastdfs initial failed." << endl;
return false;
}
if (!fastDFS.TrackerGetConnection()) {
cout << "tracker initial failed." << endl;
fastDFS.FdfsClientDestroy();
return false;
}
if (!fastDFS.TrackerQueryStorageStore()) {
cout << "storage initial failed." << endl;
fastDFS.FdfsClientDestroy();
return false;
}
if (!fastDFS.StorageUploadByFilename1(fileName, fileId)) {
cout << "StorageUploadByFilename1 initial failed." << endl;
fastDFS.FdfsClientDestroy();
return false;
}
if (!fastDFS.TrackerCloseConnectionEx()) {
cout << "TrackerCloseConnectionEx initial failed." << endl;
fastDFS.FdfsClientDestroy();
return false;
}
fastDFS.FdfsClientDestroy();
printf("<br>fileid: %s\n<br>", fileId);
return true;
}
具体的实现流程封装在FastDFS这个类里,到时候可以具体介绍。
随后是SaveToMysql()这个函数,它实现了将filename和传入fastDFS得到的fileid存入数据库功能,详细步骤就是拼装sql语句,调用mysql接口进行写入。
讲一下比较重要的两个函数:
bool Upload::AcceptFile() {
while (fastCGI.FcgiAccept()) {
fastCGI.contentLen = getenv("CONTENT_LENGTH");
printf("Content-type: text/html\r\n\r\n");
if (fastCGI.contentLen != NULL) {
buflen = strtol(fastCGI.contentLen, nullptr, 10);
}
if (buflen <= 0) {
LOG("No data from standard input.");
}
else {
char tmpch;
fileData = (char*)malloc(buflen);
pbegin = ptemp = fileData;
for (int i = 0; i < buflen; ++i) {
if ((tmpch = getchar()) < 0) {
LOG("receive filedata successful.");
break;
}
*ptemp = tmpch;
++ptemp;
}
pend = ptemp;
}
ParseDataAndSave();
UploadFile(fileName);
SaveToMysql();
free(fileData);
unlink(fileName);
}
return true;
}
这是数据接收函数,通过指针一个字符一个字符的接数据流,得到filedata。其数据流的大小是通过环境变量CONTENT_LENGTH得到的,而后会通过ParseDataAndSave()对数据流进一步解析:
bool Upload::ParseDataAndSave() {
ptemp = strstr(pbegin, "\r\n");
strncpy(fastCGI.boundary, pbegin, ptemp - pbegin);
ptemp += 2;
buflen -= (ptemp - pbegin);
pbegin = ptemp;
char* pfileNameBegin = strstr(pbegin, "filename=");
pfileNameBegin += strlen("filename=");
char* pfileNameEnd = strchr(++pfileNameBegin, '"');
strncpy(fileName, pfileNameBegin, pfileNameEnd - pfileNameBegin);
printf("<br>filename: %s<br>\n", fileName);
ptemp = strstr(pbegin, "\r\n");
ptemp += 2;
buflen -= (ptemp - pbegin);
pbegin = ptemp;
ptemp = strstr(pbegin, "\r\n");
ptemp += 4;
buflen -= (ptemp - pbegin);
pbegin = ptemp;
int boderLen = strlen(fastCGI.boundary);
int FullDataLen = buflen - boderLen + 1;
for (int i = 0; i < FullDataLen; ++i) {
if (*pbegin == *fastCGI.boundary) {
if (memcmp(pbegin, fastCGI.boundary, boderLen) == 0) {
break;
}
}
++pbegin;
}
swap(pbegin, ptemp);
if (ptemp == nullptr) {
ptemp = pend;
}
ptemp -= 2;
int fd = open(fileName, O_CREAT | O_WRONLY, 0664);
write(fd, pbegin, ptemp - pbegin);
close(fd);
return true;
}
该函数用指针对数据流进行了切割,因为通过CGI接收的数据是有固定格式的,所以通过其固定格式来定位切割,从而得到filename和得到文件真实的数据块,并通过文件描述符将其写成文件。
完成这些工作之后,后面几乎就是走流程了,将得到的文件传给fastDFS做处理就行了,然后将得到的filenam和fastDFS返回的fileid存入数据库,fileid就是该文件在fastDFS系统中的访问路径,通过web页面可以直接访问的。基本的文件上传流程就是如此了,功能还是比较简单的,后续还会把该系统功能丰富起来,比如加上后台log监控,加上必要的文件下载浏览等功能,emmm,任重而道远。