共计 5780 个字符,预计需要花费 15 分钟才能阅读完成。
问题:浏览器与文件服务器如何交互?
浏览器与文件服务器的交互
-
文件浏览
- 点击列表中的文件夹 → 进入子文件夹(展现子文件夹列表)
-
文件下载
- 点击列表中的文件 → 下载指标文件
-
错误处理
- 向服务器发送谬误申请 → 浏览不存在的文件夹 / 下载不存在的文件
文件浏览交互实现
实现中的要害概念
-
共享文件夹(root)
- 服务端启动时指定的共享文件夹门路(服务零碎中的门路)
-
申请门路(req)
- 文件 / 文件夹 在服务端上绝对共享文件夹的门路
-
绝对路径
AbsPath = root + "/" + req
根底函数定义
-
char *GetAbsPath(const char *relative, const char *root)
- 拼接 root 和 relative, 返回绝对路径,返回值需开释(free)
-
int isDotPath(const char *path)
- 判断 path 是否为非凡门路 “.” 或 “..”
-
int GetEntryCount(const char *path)
- 返回 path 门路下的(文件 + 文件夹)总数
编程试验:根底函数实现
static char *GetAbsPath(const char *relative, const char *root) | |
{int reLen = strlen(relative); | |
int rootLen = strlen(root); | |
char *ret = malloc(reLen + rootLen + 2); | |
if (ret) {strcpy(ret, root); | |
if ((relative[0] == '/') && (ret[rootLen-1] == '/')) ret[rootLen - 1] = 0; | |
if ((relative[0] != '/') && (ret[rootLen-1] != '/')) strcat(ret, "/"); | |
strcat(ret, relative); | |
} | |
return ret; | |
} | |
static int IsDotPath(const char *path) | |
{ | |
int ret = -1; | |
if (path) {ret = (strcmp(path, ".") == 0) || (strcmp(path, "..") == 0); | |
} | |
return ret; | |
} | |
static int GetEntryCount(const char *path) | |
{ | |
int ret = -1; | |
DIR *dirp = opendir(path); | |
if (dirp != NULL) { | |
struct dirent *dp = NULL; | |
ret = 0; | |
while ((dp = readdir(dirp)) != NULL) {if (!IsDotPath(dp->d_name)) {ret ++;} | |
} | |
} | |
closedir(dirp); | |
return ret; | |
} |
要害数据结构定义
enum { | |
TypeAll = 0x00, | |
TypeDir = 0x04, | |
TypeFile = 0x08 | |
}; | |
typdef struct { | |
const int length; | |
RowInfo data[];}FileEntry; | |
typedef struct {char link[2048]; | |
char name[255]; | |
char type[32]; | |
char size[32]; | |
char time[32]; | |
}RowInfo; |
要害函数定义
-
int MakeEntryItem(RowInfo *item, struct dirent *dp, const char *ap, const char *req);
- 依据 dp 所指向的 文件 或 文件夹 生成 RowInfo 数据结构,胜利返回 true, 失败返回 false
-
FileEntry *GetEntry(const char *req, const char *root, int type);
- 获取 root/req 门路下 文件 和 文件夹 所对应的 RowInfo 数组,返回值需开释(free)
-
char *MakeHTML(const char *req, const char *root);
- 创立 root/req 门路下 文件 和 文件夹 所形成的 HTML 页面,返回值需开释(free)
服务端实现设计
非凡页面设计
编程试验:文件浏览交互实现
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <sys/types.h> | |
#include <sys/stat.h> | |
#include <fcntl.h> | |
#include <dirent.h> | |
#include <unistd.h> | |
#include <time.h> | |
#include "page.h" | |
#include "response.h" | |
enum {TypeAll = 0x00, TypeDir = 0x04, TypeFile = 0x08}; | |
typedef struct | |
{ | |
const int length; | |
RowInfo data[];} FileEntry; | |
static char* GetAbsPath(const char* relative, const char* root) | |
{int reLen = strlen(relative); | |
int rootLen = strlen(root); | |
char* ret = malloc(reLen + rootLen + 2); | |
if(ret) {strcpy(ret, root); | |
if((relative[0] == '/') && (ret[rootLen-1] == '/') ) ret[rootLen-1] = 0; | |
if((relative[0] != '/') && (ret[rootLen-1] != '/') ) strcat(ret, "/"); | |
strcat(ret, relative); | |
} | |
return ret; | |
} | |
static int IsDotPath(const char* path) | |
{ | |
int ret = -1; | |
if(path) {ret = (strcmp(path, ".") == 0) || (strcmp(path, "..") == 0); | |
} | |
return ret; | |
} | |
static int GetEntryCount(const char* path) | |
{ | |
int ret = -1; | |
DIR* dirp = opendir(path); | |
if(dirp != NULL) { | |
struct dirent* dp = NULL; | |
ret = 0; | |
while((dp = readdir(dirp)) != NULL ) {if( !IsDotPath(dp->d_name) ) {ret++;} | |
} | |
} | |
closedir(dirp); | |
return ret; | |
} | |
static void SortFileEntry(FileEntry* fe) | |
{RowInfo* temp = malloc(sizeof(*temp)); | |
if(fe && temp) { | |
int i = 0; | |
int j = 0; | |
for(i=0; i<fe->length; i++) { | |
int min = i; | |
for(j=i; j<fe->length; j++) {if( strcmp(fe->data[min].name, fe->data[j].name) > 0 ) {min = j;} | |
} | |
*temp = fe->data[i]; | |
fe->data[i] = fe->data[min]; | |
fe->data[min] = *temp; | |
} | |
} | |
free(temp); | |
} | |
static int MakeEntryItem(RowInfo* item, struct dirent* dp, const char* ap, const char* req) | |
{ | |
int ret = 0; | |
char buf[32] = {0}; | |
struct stat sb = {0}; | |
char* path = GetAbsPath(dp->d_name, ap); | |
if(path && (ret = (stat(path, &sb)) != -1) ) | |
{strcpy(item->link, req); | |
(strcmp(req, "/") != 0) ? strcat(item->link, "/") : 0; | |
strcat(item->link, dp->d_name); | |
strcpy(item->name, dp->d_name); | |
if(dp->d_type == TypeFile) {strcpy(item->type, "File"); | |
if(sb.st_size < 1024) {sprintf(buf, "%ld", sb.st_size); | |
strcpy(item->size, buf); | |
strcat(item->size, "Byte"); | |
} | |
else if((sb.st_size / 1024) < 1024 ) {sprintf(buf, "%ld", sb.st_size/1024); | |
strcpy(item->size, buf); | |
strcat(item->size, "KB"); | |
} | |
else {sprintf(buf, "%ld", sb.st_size/1024/1024); | |
strcpy(item->size, buf); | |
strcat(item->size, "MB"); | |
} | |
} | |
else {strcpy(item->type, "Folder"); | |
sprintf(buf, "%d", GetEntryCount(path)); | |
strcpy(item->size, buf); | |
strcat(item->size, "Item"); | |
} | |
strcpy(item->time, ctime(&sb.st_mtime)); | |
} | |
free(path); | |
return ret; | |
} | |
static FileEntry* GetEntry(const char* req, const char* root, int type) | |
{char* ap = GetAbsPath(req, root); | |
DIR* dirp = NULL; | |
FileEntry* ret = NULL; | |
if(ap && (dirp = opendir(ap)) ) { | |
const int STEP = 5; | |
struct dirent* dp = NULL; | |
int max = 0; | |
int* pLen = NULL; | |
ret = malloc(sizeof(*ret)); | |
if(ret) {pLen = (int*)&ret->length; | |
*pLen = 0; | |
} | |
while(pLen && ((dp = readdir(dirp)) != NULL) ) {if( *pLen == max) { | |
max = max + STEP; | |
ret = realloc(ret, sizeof(*ret) + sizeof(RowInfo) * max); | |
pLen = (int*)&ret->length; | |
} | |
if(ret && ((type == TypeAll) || (type == dp->d_type)) ) {if( !IsDotPath(dp->d_name) && MakeEntryItem(&ret->data[*pLen], dp, ap, req) ) | |
{*pLen = *pLen + 1;} | |
} | |
} | |
SortFileEntry(ret); | |
} | |
free(ap); | |
closedir(dirp); | |
return ret; | |
} | |
static char* MakeHTML(const char* req, const char* root) | |
{ | |
char* ret = NULL; | |
Table* table = CreateTable(); | |
if(table) { | |
FileEntry* fe = NULL; | |
char* ts = NULL; | |
char* resp = NULL; | |
RowInfo* back = NULL; | |
int i = 0; | |
if((strcmp(req, "/") != 0) && (back = calloc(1, sizeof(*back))) ) {i = strlen(req) - 1; | |
strcpy(back->link, req); | |
while(back->link[i] != '/' ) i--; | |
i ? (back->link[i] = 0) : (back->link[i+1] = 0); | |
strcpy(back->name, "./.."); | |
strcpy(back->type, "Back.."); | |
table = InsertRow(table, back); | |
} | |
free(back); | |
fe = GetEntry(req, root, TypeDir); | |
for(i=0; fe && (i<fe->length); i++) {table = InsertRow(table, &fe->data[i]); | |
} | |
free(fe); | |
fe = GetEntry(req, root, TypeFile); | |
for(i=0; fe && (i<fe->length); i++) {table = InsertRow(table, &fe->data[i]); | |
} | |
free(fe); | |
ts = ToTableString(table); | |
ret = ts ? ToPageString(req, ts) : NULL; | |
free(ts); | |
} | |
FreeTable(table); | |
return ret; | |
} | |
static int Response(TcpClient* client, const char* html) | |
{ | |
const char* HTTP_FORMAT = "HTTP/1.1 200 OK\r\n" | |
"Server:Test Http Server\r\n" | |
"Content-Length:%d\r\n" | |
"Content-Type:text/html\r\n" | |
"Connection:close\r\n\r\n" | |
"%s"; | |
int ret = 0; | |
if(html) {char* resp = malloc(strlen(HTTP_FORMAT) + strlen(html) + 16); | |
if(resp) {sprintf(resp, HTTP_FORMAT, strlen(html), html); | |
ret = (TcpClient_SendRaw(client, resp, strlen(resp)) > 0); | |
} | |
free(resp); | |
} | |
return ret; | |
} | |
static int DirReqHandler(TcpClient* client, const char* req, const char* root) | |
{char* html = MakeHTML(req, root); | |
int ret = Response(client, html); | |
free(html); | |
return ret; | |
} | |
int RequestHandler(TcpClient* client, const char* req, const char* root) | |
{ | |
int ret = 0; | |
if(client && req && root) {ret = DirReqHandler(client, req, root); | |
} | |
return ret; | |
} |
思考
favicon.ico 是什么?如何解决?
正文完