总体来说,goahead实现文件下载功能比较简单,基本上是调用固定的函数,但是也有一些坑,下面详细讲解我的实例记录。
(1)定义下载的回调函数
websDefineAction("downloadConfigFile", downloadConfigFile);
(2)下载回调函数的实现
static void downloadConfigFile(Webs *wp, char *path, char *query){
WebsHandlerProc service = (*wp).route->handler->service;
(*wp).route->handler->close = (*avolfileClose);
(*wp).route->handler->service =(*ConfigfileHandler);
(*wp).route->handler->service(wp);
(*wp).route->handler->service= service;
}
上面函数的关键部分是ConfigfileHandler的实现,具体实现见(3)
(3)关键函数实现
关键点在下面函数的分割线处
static bool ConfigfileHandler(Webs *wp){
WebsFileInfo info;
char *tmp, *date;
ssize nchars;
int code;
char* pathfilename; //带路径的文件名 用于找到对应的文件
char* filenameExt; //文件扩展名 用于 设置 MIME类型
char* filename; //文件名 用于下载后保存的文件名称
char* disposition; //临时保存 附件 标识
char *filebegin;
char *filend;
char file_name_arr[50];
assert(websValid(wp));
assert(wp->method);
assert(wp->filename && wp->filename[0]);
// ******************************分割线******************************//
//下面语句的websGetVar的第二个参数"filepath"与最后一步的具体的下载操作有关系
pathfilename = websGetVar(wp, "filepath", NULL);
if (pathfilename==NULL)
return true;
//取文件名和扩展名
filebegin = strrchr(pathfilename,'/');
filend = strchr(pathfilename,'.');
memset(file_name_arr,0,50);
snprintf(file_name_arr,strlen(filebegin) - strlen(filend) - 1,"%s",filebegin + 1);
// strncpy(file_name_arr,filebegin + 1, strlen(filebegin) - strlen(filend) - 1);
filename =sclone("config");
filenameExt =sclone("tar.gz");
if (wp->ext) wfree(wp->ext);
wp->ext=(char*)walloc(1+strlen(filenameExt)+1);
sprintf(wp->ext,".%s",sclone(filenameExt));
if (wp->filename) {
wfree(wp->filename);
}
wp->filename=sclone(pathfilename);
if (wp->path) {
wfree(wp->path);
}
wp->path=sclone(pathfilename);
//If the file is a directory, redirect using the nominated default page
if (websPageIsDirectory(wp)) {
nchars = strlen(wp->path);
if (wp->path[nchars - 1] == '/' || wp->path[nchars - 1] == '\\') {
wp->path[--nchars] = '\0';
}
char* websIndex = "testdownload";
tmp = sfmt("%s/%s", wp->path, websIndex);
websRedirect(wp, tmp);
wfree(tmp);
return true;
}
if (websPageOpen(wp, O_RDONLY | O_BINARY, 0666) < 0) {
websError(wp, HTTP_CODE_NOT_FOUND, "Cannot open document for: %s", wp->path);
return true;
}
if (websPageStat(wp, &info) < 0) {
websError(wp, HTTP_CODE_NOT_FOUND, "Cannot stat page for URL");
return true;
}
code = 200;
if (wp->since && info.mtime <= wp->since) {
code = 304;
}
websSetStatus(wp, code);
websWriteHeaders(wp, info.size, 0);
//浏览器下载文件时的文件名
disposition = (char*)walloc(20+strlen(filename)+ strlen(filenameExt)+1);
sprintf(disposition,"attachment;filename=%s.%s",sclone(filename),sclone(filenameExt));
websWriteHeader(wp, "Content-Disposition", sclone(disposition));
free(filename);
free(disposition);
filename=NULL;
disposition=NULL;
free(filenameExt);
filenameExt=NULL;
if ((date = websGetDateString(&info)) != NULL) {
websWriteHeader(wp, "Last-modified", "%s", date);
wfree(date);
}
websWriteEndHeaders(wp);
//All done if the browser did a HEAD request
if (smatch(wp->method, "HEAD")) {
websDone(wp);
return true;
}
websSetBackgroundWriter(wp, fileWriteEvent);
return true;
}
(4)其他函数实现
static void fileWriteEvent(Webs *wp)
{
char *buf;
ssize len, wrote;
int err;
assert(wp);
assert(websValid(wp));
if ((buf = walloc(ME_GOAHEAD_LIMIT_BUFFER)) == NULL) {
websError(wp, HTTP_CODE_INTERNAL_SERVER_ERROR, "Cannot get memory");
return;
}
while ((len = websPageReadData(wp, buf, ME_GOAHEAD_LIMIT_BUFFER)) > 0) {
if ((wrote = websWriteSocket(wp, buf, len)) < 0) {
err = socketGetError(wp->sid);
if (err == EWOULDBLOCK || err == EAGAIN) {
websPageSeek(wp, -len, SEEK_CUR);
} else {
/* Will call websDone below */
wp->state = WEBS_COMPLETE;
}
break;
}
if (wrote != len) {
websPageSeek(wp, - (len - wrote), SEEK_CUR);
break;
}
}
wfree(buf);
if (len <= 0) {
websDone(wp);
}
}
static bool avolfileHandler(Webs *wp){
WebsFileInfo info;
char *tmp, *date;
ssize nchars;
int code;
char* pathfilename; //带路径的文件名 用于找到对应的文件
char* filenameExt; //文件扩展名 用于 设置 MIME类型
char* filename; //文件名 用于下载后保存的文件名称
char* disposition; //临时保存 附件 标识
assert(websValid(wp));
assert(wp->method);
assert(wp->filename && wp->filename[0]);
// 取download.lua?log=C:\aa.log 中的 C:\aa.log
pathfilename = websGetVar(wp, "log", NULL);
if (pathfilename==NULL)
return true;
//取文件名和扩展名
filename =sclone("aa");
filenameExt =sclone("log");
if (wp->ext) wfree(wp->ext);
wp->ext=(char*)walloc(1+strlen(filenameExt)+1);
sprintf(wp->ext,".%s",sclone(filenameExt));
if (wp->filename) {
wfree(wp->filename);
}
wp->filename=sclone(pathfilename);
if (wp->path) {
wfree(wp->path);
}
wp->path=sclone(pathfilename);
//If the file is a directory, redirect using the nominated default page
if (websPageIsDirectory(wp)) {
nchars = strlen(wp->path);
if (wp->path[nchars - 1] == '/' || wp->path[nchars - 1] == '\\') {
wp->path[--nchars] = '\0';
}
char* websIndex = "testdownload";
tmp = sfmt("%s/%s", wp->path, websIndex);
websRedirect(wp, tmp);
wfree(tmp);
return true;
}
if (websPageOpen(wp, O_RDONLY | O_BINARY, 0666) < 0) {
websError(wp, HTTP_CODE_NOT_FOUND, "Cannot open document for: %s", wp->path);
return true;
}
if (websPageStat(wp, &info) < 0) {
websError(wp, HTTP_CODE_NOT_FOUND, "Cannot stat page for URL");
return true;
}
code = 200;
if (wp->since && info.mtime <= wp->since) {
code = 304;
}
websSetStatus(wp, code);
websWriteHeaders(wp, info.size, 0);
//浏览器下载文件时的文件名
disposition = (char*)walloc(20+strlen(filename)+ strlen(filenameExt)+1);
sprintf(disposition,"attachment;filename=%s.%s",sclone(filename),sclone(filenameExt));
websWriteHeader(wp, "Content-Disposition", sclone(disposition));
free(filename);
free(disposition);
filename=NULL;
disposition=NULL;
free(filenameExt);
filenameExt=NULL;
if ((date = websGetDateString(&info)) != NULL) {
websWriteHeader(wp, "Last-modified", "%s", date);
wfree(date);
}
websWriteEndHeaders(wp);
//All done if the browser did a HEAD request
if (smatch(wp->method, "HEAD")) {
websDone(wp);
return true;
}
websSetBackgroundWriter(wp, fileWriteEvent);
return true;
}
(5)下载文件操作
在浏览器输入:http://10.10.10.10:8080/action?filepath=/tmp/111.jpg即可下载相应文件
对上述 输入内容的说明:http://10.10.10.10(具体服务器的IP):8080(端口号)/action(固定为action)/downloadConfigFile(对应于注册回调函数时的字符串)?filepath(对应于分割线处的函数入参)=/tmp/111.jpg(具体的文件路径)
(6)上述内容部分参考自:goahead下载文件 - 走看看 (zoukankan.com),致谢。欢迎同行相互讨论赐教。