文件传输工具FTransferor<优化篇>

在上一篇文章中,我们详细探讨了FTransferor文件传输工具的设计与实现,并展示了它在局域网文件传输方面的高效性。然而,随着互联网应用场景的不断丰富,传统的基于 TCP/UDP 的传输方式已经无法满足部分开发者的需求。特别是在跨平台、跨网络传输场景中,对 HTTP 协议的支持变得尤为重要。因此,本篇文章将围绕 FTransferor 的优化改造,为其增加 HTTP 协议支持,并提升其在复杂网络环境下的适用性。

为什么要支持 HTTP 协议?

HTTP 是一种通用性极强的传输协议,具备以下特点:

1)广泛的兼容性:几乎所有平台都支持 HTTP 协议,可以无缝集成到现有的系统中。

2)易用性:基于 HTTP 的文件传输可以通过浏览器直接访问,无需额外安装客户端工具。

通过为 FTransferor 增加 HTTP 协议支持,我们可以大幅提升工具的实用性,使其不仅适用于局域网场景,还能满足跨网络的传输需求。

功能上的补充

在上一篇中我们对文件传输工具的功能仅支持上传文件,在增加HTTP协议支持的同时,还对下载、查看文件列表 等功能进行了丰富,并且利用密码支持了一定程度的安全性。

具体实现

增加HTTP Server并实现一个简单的拦截器

首先是HTTP Server,这个实现相对简单,主要就是文件下载和文件列表查看两个HTTP处理器。

go 复制代码
const (
    PathList      = "/files"
    PathDownload  = "/download/"
    QueryParamKey = "secret"
)

func runHttpServer(port int) {
    fn := "runHttpServer"
    http.HandleFunc(PathList, secretFilterHandler(fileListHandler))
    http.HandleFunc(PathDownload, secretFilterHandler(fileDownloadHandler))

    fmt.Printf("%s is listening on port %d\n", fn, port)
    if err := http.ListenAndServe(fmt.Sprintf(":%d", port), nil); err != nil {
        fmt.Println("Error starting file server:", err)
    }
}

拦截器实现,主要就是拦截query参数中的secret key,需要和启动HTTP Server时输入的参数一致。

go 复制代码
func secretFilterHandler(next http.HandlerFunc) func(w http.ResponseWriter, r *http.Request) {
    return func(w http.ResponseWriter, r *http.Request) {
        getSecret := r.URL.Query().Get(QueryParamKey)
        if getSecret != secret {
            http.Error(w, "Unauthorized", http.StatusUnauthorized)
            return
        }
        next(w, r)
    }
}

运行以上代码后,用户可以通过浏览器和客户端命令中查看和下载当前目录中的文件,客户端的实现请继续往下看。

HTTP Server端具体实现

首先是文件列表查看功能的实现:

go 复制代码
func fileListHandler(w http.ResponseWriter, r *http.Request) {
    files, err := os.ReadDir(path)
    if err != nil {
        http.Error(w, "Unable to list files", http.StatusInternalServerError)
        return
	}

    result := make([]string, 0)
    w.Header().Set("Content-Type", "application/json")
    for _, f := range files {
        if !f.IsDir() {
            result = append(result, f.Name())
        }
    }

    marshal, err := json.Marshal(map[string]interface{}{
        "data": result,
    })
    if err != nil {
        http.Error(w, "Unable to list files", http.StatusInternalServerError)
        return
    }
    _, _ = w.Write(marshal)
}

其次是文件下载功能的实现:

go 复制代码
func fileDownloadHandler(w http.ResponseWriter, r *http.Request) {
    fileName := strings.TrimPrefix(r.URL.Path, PathDownload)
    filePath := filepath.Join(path, fileName)
    if _, err := os.Stat(filePath); os.IsNotExist(err) {
        http.Error(w, "File not found", http.StatusNotFound)
        return
    }
    http.ServeFile(w, r, filePath)
}
客户端实现

客户端我们可以直接使用浏览器进行操作,但是考虑到在Linux服务器的情况下可能只有终端命令行,因此也提供命令的方式进行操作,主要的思路就是增加命令参数,根据参数去访问服务端不同的HTTP接口。

代码实现:

go 复制代码
func httpClient(action Action, f string) {
    httpServerAddr := fmt.Sprintf("%s%s", Scheme, server)

    client := http.Client{}
    switch action {
    case ActionGet:
        url := fmt.Sprintf("%s%s%s?%s=%s", httpServerAddr, PathDownload, f, QueryParamKey, passwd)
        resp, err := client.Get(url)
        if err != nil {
            fmt.Println("Error downloading file:", err)
        }

        defer func() {
            _ = resp.Body.Close()
        }()

        // 创建文件以保存下载的内容
        file, err := os.Create(f)
        if err != nil {
            fmt.Println("Error creating file:", err)
            return
        }
        defer func() {
            _ = file.Close()
        }()

        // 将响应的内容写入文件
        if _, err = io.Copy(file, resp.Body); err != nil {
            fmt.Println("Error writing to file:", err)
            return
        }
        fmt.Println("File downloaded successfully!")
    case ActionList:
        url := fmt.Sprintf("%s%s?%s=%s", httpServerAddr, PathList, QueryParamKey, passwd)
        resp, err := client.Get(url)
        if err != nil {
            fmt.Println("Error downloading file:", err)
        }

        defer func() {
            _ = resp.Body.Close()
        }()

        // 读取响应的body
        body, err := io.ReadAll(resp.Body)
        if err != nil {
            fmt.Println("Error reading response body:", err)
            return
        }

        // 将body转换为map
        var data map[string]interface{}
        if err = json.Unmarshal(body, &data); err != nil {
            fmt.Println("Error unmarshaling JSON:", err)
            return
        }
        // 打印map
        fmt.Println("Data as map:", data)
    default:
    }
}
新功能演示

首先启动服务端,TCP和HTTP共同启动,但是注意不能使用同一个端口号:

shell 复制代码
./FTransferor server --path filepath --port 8081 --webport 8082 --secret D&J$HE23

然后使用客户端命令,查看文件列表:

shell 复制代码
./FTransferor.exe cli --server localhost:8082 --action list --passwd D&J$HE23

下载文件:

shell 复制代码
./FTransferor.exe cli --server localhost:8082 --action get --passwd D&J$HE23 --file t.zip
总结

通过本次优化,我们为 FTransferor 增加了对 HTTP 协议的支持,显著提升了其在不同网络环境下的适用性。未来,我们计划进一步优化工具的交互体验,例如通过 Web 界面实现更加友好的操作方式。

相关推荐
橘子海全栈攻城狮3 分钟前
【源码+文档+调试讲解】“健康早知道”微信小程序
开发语言·servlet·微信小程序·小程序·notepad++
爱是小小的癌12 分钟前
Java-数据结构-顺序表(ArrayList)
java·开发语言·数据结构
我明天再来学Web渗透20 分钟前
【2024年-11月-9日-开源社区openEuler实践记录】OpenAMDC:开启智能边缘计算与系统管控的新征程
开发语言·人工智能·架构·开源·边缘计算·copilot·开源软件
egoist202338 分钟前
数据结构之单链表(超详解)
c语言·开发语言·数据结构·笔记·学习·链表·gitee
小_太_阳40 分钟前
Scala_【3】运算符
开发语言·scala·intellij-idea
武子康41 分钟前
大数据-269 实时数仓 - DIM DW ADS 层处理 Scala实现将数据写出HBase等
java·大数据·数据仓库·后端·flink·scala·hbase
xinghuitunan43 分钟前
考试座位号(PTA)C语言
c语言·开发语言
程序边界1 小时前
AIGC赋能Java编程:智能工具引领效率、创新与理解的新纪元
java·开发语言·aigc
jk_1011 小时前
MATLAB中binopdf函数用法
开发语言·算法·matlab
木觞清2 小时前
Python 图像处理:生成美丽的书籍封面
开发语言·python