问题:浏览器与文件服务器如何交互?

浏览器与文件服务器的交互

  • 文件浏览

    • 点击列表中的文件夹 → 进入子文件夹(展示子文件夹列表)
  • 文件下载

    • 点击列表中的文件 → 下载目标文件
  • 错误处理

    • 向服务器发送错误请求 → 浏览不存在的文件夹 / 下载不存在的文件

文件浏览交互实现

image.png

实现中的关键概念

  • 共享文件夹(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)

服务端实现设计

image.png

特殊页面设计

image.png

编程实验:文件浏览交互实现


#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 是什么?如何处理?

TianSong
737 声望139 粉丝

阿里山神木的种子在3000年前已经埋下,今天不过是看到当年注定的结果,为了未来的自己,今天就埋下一颗好种子吧