GO學習記錄七——上傳/下載文件功能,添加啟動運行工具

本來計劃是學習Docker部署的,研究了一天沒搞出來,得出結論是需要翻墻,懶得弄了,暫時放置。

一、以下是,上傳/下載代碼,和之前是重復的,只多添加了,上傳/下載功能。
測試目錄為工程根目錄下/files文件夾內

package mainimport ("context""encoding/json""fmt""io""log""net/http""os""path/filepath""regexp""time""github.com/gin-gonic/gin""github.com/jackc/pgx/v5""github.com/jackc/pgx/v5/pgxpool"swaggerFiles "github.com/swaggo/files"ginSwagger "github.com/swaggo/gin-swagger"// 注意:替換為你項目的實際路徑// _ "your_project/docs" // docs 包,由 swag 生成// 如果 docs 包在根目錄,且 main.go 也在根目錄,可以這樣導入_ "HTTPServices/docs" // 假設 docs 目錄在項目根目錄下
)var db *pgxpool.Pool// 啟動函數
func main() {// 初始化數據庫連接db = InitDB()defer db.Close()// 注冊路由RegisterRouter()// 啟動 HTTP 服務go func() {StartHTTPServer()}()// 啟動 HTTP api測試服務go func() {StartDebugHTTPServer()}()// 阻塞主線程select {}
}// @title           Sample API
// @version         1.0
// @description     API測試頁面
// @host      localhost:8080
func StartDebugHTTPServer() {r := gin.Default()// --- 掛載 Swagger UI ---// 訪問 http://localhost:8081/swagger/index.html 查看 UIr.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))port := ":8081"LogSuccess("啟動 HTTP Swagger測試服務啟動,監聽端口 %s\n", port)// 啟動服務器debugApiError := r.Run(port)if debugApiError != nil {LogError("HTTP api測試服務啟動失敗:%v", debugApiError)} else {LogSuccess("HTTP api測試服務已啟動,監聽端口 8081")}
}// 啟動 HTTP 服務
func StartHTTPServer() {address := "127.0.0.1:8080" //配置連接ip端口//配置跨域,是影響調試頁面不能訪問8080相關地址的原因handler := corsMiddleware(http.DefaultServeMux)LogSuccess("啟動 HTTP 服務,監聽端口 %s\n", address)err := http.ListenAndServe(address, handler)if err != nil {log.Fatalf("服務器啟動失敗:%v", err)}
}// corsMiddleware 是一個中間件,用于添加 CORS 頭
func corsMiddleware(next http.Handler) http.Handler {return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {// 設置 CORS 響應頭w.Header().Add("Access-Control-Allow-Origin", "http://localhost:8081") // ? 修改為你的前端地址w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, PATCH")w.Header().Set("Access-Control-Allow-Headers","Origin, Content-Type, Accept, Authorization, X-Requested-With")// 如果需要傳遞 Cookie 或 Authorization Bearer Tokenw.Header().Set("Access-Control-Allow-Credentials", "true")// 處理預檢請求 (OPTIONS)if r.Method == "OPTIONS" {w.WriteHeader(http.StatusOK)return}// 調用下一個處理器 (即注冊的路由)next.ServeHTTP(w, r)})
}// 注冊路由
func RegisterRouter() {http.HandleFunc("/", helloHandler)    //http://localhost:8080/http.HandleFunc("/time", timeHandler) //http://localhost:8080/time//查詢http.HandleFunc("/findTable", findTableNameHandler) //http://localhost:8080/findTable?tableName=name//添加http.HandleFunc("/addTable1", addTable1Handler) //http://localhost:8080/addTable1//刪除http.HandleFunc("/deleteTableValue", deleteTableHandler) //http://localhost:8080/deleteTableValue?tableName=table1&fieldName=test1&fieldValue=123test//修改http.HandleFunc("/updateTableValue", updateTableHandler) //http://localhost:8080/updateTableValue?tableName=table1&findFieldName=test1&findFieldValue=hello&setFieldName=test3&setFieldValue=456//下載文件http.HandleFunc("/downloadFile", downloadHandler) //http://localhost:8080/downloadFile?filePath=D:\GoProject\HTTPServices\README.md//上傳文件http.HandleFunc("/uploadFile", uploadHandler) //http://localhost:8080/uploadFile}// APIResponse 定義了統一的 API 響應格式
type APIResponse struct {Success   bool        `json:"success"`           // 是否成功Status    int         `json:"status"`            // HTTP 狀態碼Message   string      `json:"message,omitempty"` // 簡短消息 報錯時的提示信息Data      interface{} `json:"data,omitempty"`    // 主要數據內容Timestamp string      `json:"timestamp"`         // 時間戳 (秒)
}// SendJSONResponse 封裝了 JSON 響應的發送邏輯
func SendJSONResponse(w http.ResponseWriter, success bool, status int, message string, data interface{}) {// 設置 Content-Typew.Header().Set("Content-Type", "application/json")// 設置 HTTP 狀態碼w.WriteHeader(status)// 構造響應體response := APIResponse{Success:   success,Status:    status,Message:   message,Data:      data,Timestamp: time.Now().Format("2006-01-02 15:04:05"), // 當前時間戳格式化}// 編碼并發送 JSONif err := json.NewEncoder(w).Encode(response); err != nil {// 如果編碼失敗,記錄錯誤(但不能再次寫入 w,因為 Header 已經發送)http.Error(w, "Internal Server Error", http.StatusInternalServerError)// log.Printf("JSON encode error: %v", err) // 取消注釋以記錄日志}
}// @Summary      根目錄測試連接
// @Description
// @Tags         tags1
// @Accept       json
// @Produce      json
// @Router       / [get]
func helloHandler(w http.ResponseWriter, r *http.Request) {LogInfo("訪問路徑:%s,來源:%s\n", r.URL.Path, r.RemoteAddr)// 編碼 JSON 響應SendJSONResponse(w, true, http.StatusOK, "成功", "Hello, World! 👋")
}// @Summary      查詢服務器時間
// @Description
// @Tags         tags1
// @Accept       json
// @Produce      json
// @Router       /time [get]
func timeHandler(w http.ResponseWriter, r *http.Request) {LogInfo("訪問路徑:%s,來源:%s\n", r.URL.Path, r.RemoteAddr)currentTime := time.Now().Format("2006-01-02 15:04:05")// ? 設置響應頭SendJSONResponse(w, true, http.StatusOK, "成功", currentTime)
}// @Summary      修改指定表名中,find字段名等于指定值的set字段名的數據
// @Description  根據提供的表名、find字段名、find字段值、set字段名、set字段值,修改數據庫中的數據。
// @Tags         tags1
// @Produce      json
// @Param        tableName query string true  "要查詢的數據庫表名" default(table1)
// @Param        fieldName query string true  "要查詢的字段名"
// @Param        fieldValue query string true  "要查詢的字段值"
// @Param        setFieldName query string true  "要更新的字段名"
// @Param        setFieldValue query string true  "要更新的字段值"
// @Router       /updateTableValue [get]
func updateTableHandler(w http.ResponseWriter, r *http.Request) {// 解析請求參數tableName := r.URL.Query().Get("tableName")findFieldName := r.URL.Query().Get("findFieldName")findFieldValue := r.URL.Query().Get("findFieldValue")setFieldName := r.URL.Query().Get("setFieldName")setFieldValue := r.URL.Query().Get("setFieldValue")// 完整的參數驗證if tableName == "" || findFieldName == "" || setFieldName == "" {http.Error(w, "缺少必要參數", http.StatusBadRequest)return}// 🔐 白名單驗證 - 只允許預定義的表和字段allowedTables := map[string]bool{"table1": true, "table2": true}allowedFields := map[string]bool{"test1": true, "test2": true, "test3": true,"test4": true, "test5": true, "test6": true, "test7": true,}if !allowedTables[tableName] {http.Error(w, "不允許的表名", http.StatusBadRequest)return}if !allowedFields[findFieldName] || !allowedFields[setFieldName] {http.Error(w, "不允許的字段名", http.StatusBadRequest)return}// ? 使用參數化查詢,表名和字段名通過白名單驗證后拼接query := fmt.Sprintf("UPDATE %s SET %s = $1 WHERE %s = $2",tableName, setFieldName, findFieldName,)result, err := db.Exec(context.Background(), query, setFieldValue, findFieldValue)if err != nil {http.Error(w, "更新數據失敗: "+err.Error(), http.StatusInternalServerError)return}// 檢查是否實際更新了數據rowsAffected := result.RowsAffected()if rowsAffected == 0 {http.Error(w, "未找到匹配的數據進行更新", http.StatusNotFound)return}SendJSONResponse(w, true, http.StatusOK, "成功", fmt.Sprintf("%d 行已更新", rowsAffected))}// @Summary      刪除指定表名中,指定字段名等于指定值的數據
// @Description  根據提供的表名和字段名和值,刪除數據庫中的數據。
// @Tags         tags1
// @Produce      json
// @Param        tableName query string true  "要刪除的數據庫表名"
// @Param        fieldName query string true  "要刪除的字段名"
// @Param        fieldValue query string true  "要刪除的字段值"
// @Router       /deleteTableValue [get]
func deleteTableHandler(w http.ResponseWriter, r *http.Request) {// 解析請求參數tableName := r.URL.Query().Get("tableName")fieldName := r.URL.Query().Get("fieldName")fieldValue := r.URL.Query().Get("fieldValue")if tableName == "" || fieldName == "" || fieldValue == "" {http.Error(w, "參數錯誤", http.StatusBadRequest)return}// 執行 SQL 語句,使用參數化查詢query := fmt.Sprintf("DELETE FROM %s WHERE %s = $1", tableName, fieldName)_, err := db.Exec(context.Background(), query, fieldValue)if err != nil {http.Error(w, "刪除數據失敗: "+err.Error(), http.StatusInternalServerError)return}SendJSONResponse(w, true, http.StatusOK, "成功", "數據已刪除")
}// @Summary      向table1表中添加數據,字段名=test1,test2,test3,test4,test5,test6,test7
// @Description  根據提供的json數據,向數據庫table1中添加數據。
// @Tags         tags1
// @Produce      json
// @Param        data body string true "要插入的數據對象"
// @Router       /addTable1 [post]
func addTable1Handler(w http.ResponseWriter, r *http.Request) {// 定義需要插入的數據結構type requestData struct {Test1 string    `json:"test1"`Test2 time.Time `json:"test2"`Test3 uint32    `json:"test3"`Test4 string    `json:"test4"`Test5 float64   `json:"test5"`Test6 int32     `json:"test6"`Test7 float64   `json:"test7"`}// 解析請求參數var data requestDataerr := json.NewDecoder(r.Body).Decode(&data)if err != nil {http.Error(w, "解析請求參數失敗: "+err.Error(), http.StatusBadRequest)return}// 執行 SQL 語句,使用參數化查詢query := "INSERT INTO table1 (test1, test2, test3, test4, test5, test6, test7) VALUES ($1, $2, $3, $4, $5, $6, $7)"_, err = db.Exec(context.Background(), query, data.Test1, data.Test2, data.Test3, data.Test4, data.Test5, data.Test6, data.Test7)if err != nil {http.Error(w, "插入數據失敗: "+err.Error(), http.StatusInternalServerError)return}SendJSONResponse(w, true, http.StatusOK, "成功", "數據已插入")
}// @Summary      查詢指定表名的全部數據
// @Description  根據提供的表名查詢數據庫中的所有數據。
// @Tags         tags1
// @Produce      json
// @Param        tableName query string true  "要查詢的數據庫表名" default(table1)
// @Router       /findTable [get]
func findTableNameHandler(w http.ResponseWriter, r *http.Request) {tableName := r.URL.Query().Get("tableName")if tableName == "" {http.Error(w, "tableName is empty", http.StatusBadRequest)return}// ? 安全校驗表名(防止 SQL 注入)if !isValidTableName(tableName) {http.Error(w, "invalid table name", http.StatusBadRequest)return}// ? 使用參數化方式拼接表名(僅限對象名,如表、字段)query := fmt.Sprintf("SELECT * FROM %s", tableName)rows, err := db.Query(context.Background(), query)if err != nil {http.Error(w, "查詢失敗: "+err.Error(), http.StatusInternalServerError)return}defer rows.Close()// ? 使用 pgx 內置工具自動轉為 []map[string]interface{}data, err := pgx.CollectRows(rows, pgx.RowToMap)if err != nil {http.Error(w, "解析數據失敗: "+err.Error(), http.StatusInternalServerError)return}SendJSONResponse(w, true, http.StatusOK, "成功", data)
}// 安全校驗表名(防止 SQL 注入)
func isValidTableName(name string) bool {// 只允許字母、數字、下劃線,且不能以數字開頭matched, _ := regexp.MatchString(`^[a-zA-Z_][a-zA-Z0-9_]*$`, name)return matched
}// @Summary      下載文件
// @Description
// @Tags         tags1
// @Produce      json
// @Router       /downloadFile [get]
func downloadHandler(w http.ResponseWriter, r *http.Request) {fileName := "testdownloadFIle.txt"// 要下載的文件路徑filePath := "./files/" + fileName // 假設文件在項目根目錄下的 files 文件夾中// 設置響應頭,提示瀏覽器下載(可選)w.Header().Set("Content-Disposition", "attachment; filename="+fileName)// 如果不設置,瀏覽器可能會嘗試直接打開文件(如PDF、圖片)// 使用 http.ServeFile 提供文件http.ServeFile(w, r, filePath)
}// uploadHandler 處理文件上傳
// @Summary      上傳文件
// @Description  支持上傳任意文件,最大 50MB
// @Tags         tags1
// @Accept       multipart/form-data
// @Produce      json
// @Param        file formData file true "要上傳的文件"
// @Success      200 {string} string "文件上傳成功"
// @Failure      400 {object} map[string]string "請求錯誤,如文件太大或格式錯誤"
// @Failure      500 {object} map[string]string "服務器內部錯誤"
// @Router       /uploadFile [post]
func uploadHandler(w http.ResponseWriter, r *http.Request) {if r.Method != "POST" {http.Error(w, "只支持POST方法", http.StatusMethodNotAllowed)return}// 解析 multipart form  文件大小限制if err := r.ParseMultipartForm(50 << 20); err != nil { // 50MB限制http.Error(w, "文件太大", http.StatusBadRequest)return}// 獲取文件file, handler, err := r.FormFile("file")if err != nil {http.Error(w, "獲取文件失敗: "+err.Error(), http.StatusBadRequest)return}defer file.Close()// 創建目標文件filename := fmt.Sprintf("%d_%s", time.Now().UnixNano(), handler.Filename)dstPath := filepath.Join("./files/", filename)dst, err := os.Create(dstPath)if err != nil {http.Error(w, "創建文件失敗: "+err.Error(), http.StatusInternalServerError)return}defer dst.Close()// 復制文件內容if _, err := io.Copy(dst, file); err != nil {http.Error(w, "保存文件失敗: "+err.Error(), http.StatusInternalServerError)return}// 返回響應SendJSONResponse(w, true, http.StatusOK, "成功", "文件上傳成功")
}

二、便捷初始化swagger+直接啟動服務小工具。
測試的時候發現總是忘記執行swagger的初始化命令,導致swagger界面不出現新添加的接口,還要反應半天才明白過來是沒有執行 swag init。
直接搞了個啟動腳本,在工程根目錄創建runTools.bat文件

@echo off
:: 設置代碼頁為 UTF-8,正確顯示中文
chcp 65001 >nul
swag init
if %ERRORLEVEL% EQU 0 (echo ===swagger 初始化完成!===
go run .
) else (echo ===swagger 初始化失敗!===
)

這樣之后每次運行服務,只要在終端輸入.\runTools.bat即可。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/96516.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/96516.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/96516.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

SQL中對視圖的操作命令匯總

以下是基于搜索結果整理的SQL視圖操作命令匯總&#xff0c;按功能分類說明&#xff1a; 一、創建視圖 使用 CREATE VIEW 語句定義視圖&#xff0c;需指定視圖名稱和基礎查詢表達式&#xff1a; CREATE VIEW view_name AS SELECT column1, column2, ... FROM table_name WHER…

【Spring Cloud 微服務】2.守護神網關Gateway

目錄 1.API網關的作用 2.Spring Cloud Gateway 是什么&#xff1f; 3.核心由來與背景 1. 微服務架構的挑戰&#xff1a; 2. API 網關模式的興起&#xff1a; 3. Zuul 的局限性&#xff1a; 4. Spring Cloud Gateway 的誕生&#xff1a; 4.核心特征&#xff1a; 5.核心概…

解讀商業智能BI,數據倉庫中的元數據

之前的文章討論過數據分析、數據治理、數據倉庫等等&#xff0c;即使是非業內人員從字面意思&#xff0c;也是可以了解一二的&#xff0c;但是&#xff0c;很多人對于元數據可能就比較陌生了。那么&#xff0c;今天我們就來聊一聊元數據管理。數據倉庫要說元數據&#xff0c;那…

3 種無誤的方式刪除 Itel 手機上的短信

如果你希望釋放存儲空間、保護隱私&#xff0c;或者準備出售或轉讓手機&#xff0c;刪除 Itel 手機上的短信是一個實用的步驟。無論是收件箱中充斥著垃圾短信、過時的對話還是敏感內容&#xff0c;刪除不需要的短信可以讓你的消息體驗更加干凈和安全。本文將向你介紹 3 種簡單且…

【學習筆記】網絡安全專用產品類別與參考標準

一、基本標準 1.1 關鍵設備 網絡關鍵設備認證依據的強制標準為 GB 40050-2021。 1.2 專用產品 網絡安全專用產品認證依據的強制標準為 GB 42250-2022。 二、數據備份與恢復產品標準 相關標準&#xff1a; GB/T 29765-2021《信息安全技術 數據備份與恢復產品技術要求與測試評…

Pytho“張量”(Tensor)和 Java的“向量”(Vector)區別和聯系

在Python和Java中&#xff0c;“張量”&#xff08;Tensor&#xff09;和“向量”&#xff08;Vector&#xff09;是兩個不同語境下的概念&#xff0c;它們的設計目標、功能和應用場景存在顯著差異&#xff0c;但也存在一定的共性。以下從區別和聯系兩方面詳細說明&#xff1a;…

Ubuntu部署K8S集群

Ubuntu部署K8S集群 本例以三臺Ubuntu24.04為例,1master節點2worker節點 環境準備 修改hostname,三臺服務器分別執行 hostnamectl set-hostname k8s-master01hostnamectl set-hostname k8s-worker01hostnamectl set-hostname k8s-worker02 配置靜態ip(不同系統修改方法略微差…

openEuler系統安裝Ascend Docker Runtime的方法

在openEuler系統中使用NPU前一定要安裝Ascend Docker Runtime,也是在安裝CANN和mis-tei前的必備工作。 使用容器化支持、整卡調度、靜態vNPU調度、動態vNPU調度、斷點續訓、彈性訓練、推理卡故障恢復或推理卡故障重調度的用戶,必須安裝Ascend Docker Runtime。 下面是具體的安…

控制對文件的訪問:Linux 文件系統權限管理總結

在 Linux 系統中&#xff0c;文件權限是保障系統安全和數據完整性的核心機制。紅帽企業 Linux 9.0通過一套靈活且精細的權限控制體系&#xff0c;讓用戶能夠精確管理文件和目錄的訪問范圍。本章將系統梳理 Linux 文件系統權限的核心概念、管理方法及高級應用&#xff0c;為系統…

ansible中roles角色是什么意思?

文章目錄一、介紹二、Ansible Roles目錄編排三、創建role四、playbook調用角色五、roles中tags使用免費個人運維知識庫&#xff0c;歡迎您的訂閱&#xff1a;literator_ray.flowus.cn 一、介紹 角色是ansible自1.2版本引入的新特性&#xff0c;用于層次性、結構化地組織playbo…

pytorch 網絡可視化

1.torchsummary在 Anaconda prompt 中進入自己的 pytorch 環境&#xff0c;安裝依賴包。 bash pip install torchsummary 2.tensorboardX 3. graphviz torchviz 4.Jupyter Notebook tensorwatch 5.netron 6.hiddenlayer 7.PlotNeuralNet

可以一鍵生成PPT的AI PPT工具(最新整理)

在當今快節奏的職場環境中&#xff0c;高效制作專業PPT已成為一項必備技能。傳統PPT制作流程耗時費力&#xff0c;從構思大綱、搜集資料、撰寫內容到設計排版&#xff0c;往往需要數小時甚至數天時間。AI生成PPT工具的興起徹底改變了這一局面&#xff0c;讓職場人士能夠專注于內…

數倉核心概念闡述

數倉核心概念闡述一、數據倉庫建模模型二、數據處理架構三、流批處理架構演進**為什么需要流批融合&#xff1f;****1. Lambda 架構&#xff08;雙引擎護航&#xff09;****2. Kappa 架構&#xff08;流處理一統江湖&#xff09;****關鍵概念對照表****實際案例理解****演進趨勢…

Spring Boot 自動配置全流程深度解析

在 Spring Boot 的世界里&#xff0c;“約定優于配置” 理念通過自動配置機制展現得淋漓盡致。從一個簡單的SpringBootApplication注解開始&#xff0c;背后隱藏著一套精妙的自動配置加載流程。本文將從SpringBootApplication出發&#xff0c;逐步拆解自動配置類是如何被發現、…

AI:業務驅動與技術賦能:企業智能化應用的雙向進化深度指南

一、業務與技術的雙螺旋進化模型 1.1 從單向適配到雙向驅動的認知轉變 傳統的信息化建設往往遵循"業務提需求、技術做實現"的線性模式&#xff0c;這種模式在穩定的業務環境中確實有效&#xff0c;但在當前快速變化的數字化時代已經顯露出明顯的局限性。真正的數字化…

2721. 【SDOI2010】外星千足蟲

2721. 【SDOI2010】外星千足蟲 題解 題目描述 題目描述 公元2089年6月4日&#xff0c;在經歷了17年零3個月的漫長旅行后&#xff0c;“格納格魯一號”載人火箭返回艙終于安全著陸。此枚火箭由美國國家航空航天局&#xff08;NASA&#xff09;研制發射&#xff0c;行經火星、…

[RestGPT] RestGPT智能體

第3章&#xff1a;RestGPT智能體 歡迎回來&#x1f43b;??? 在第1章&#xff1a;配置與環境中&#xff0c;我們為RestGPT配備了必要的"鑰匙和密碼"&#xff1b;在第2章&#xff1a;OpenAPI規范(OAS)中&#xff0c;我們為它提供了與在線服務對話的"使用說明…

筆記本電腦Windows+Ubuntu 雙系統,Ubuntu無法掛載Windows的硬盤 報錯問題解決

目錄 一、前情提要 二、解決方案步驟 第一步&#xff1a;進入Windows進行修復和檢查。這是最關鍵的一步&#xff0c;目的是讓Windows來檢查和修復它自己的文件系統。 第二步&#xff1a;回到Ubuntu驗證掛載 三、總結與預防 一、前情提要 網上找到許多解決方案&#xff0c…

加密貨幣與區塊鏈:六大刑事重災區

高鵬律師&#xff08;首席數據官&#xff09;數字經濟團隊創作&#xff0c;AI輔助在數字貨幣的世界里&#xff0c;一夜暴富的傳說屢見不鮮&#xff0c;但頃刻間失去所有的悲劇也時有發生&#xff0c;現在&#xff0c;我將為您剖析加密貨幣與區塊鏈領域的六大刑事風險重災區&…

Spring Ai 1.0.1中存在的問題:使用MessageChatMemoryAdvisor導致System未被正確的放在首位

使用MessageChatMemoryAdvisor導致System未被正確的放在首位 如下是使用Spring Ai實現多輪對話的官方例子&#xff08;文檔地址&#xff1a;https://docs.spring.io/spring-ai/reference/api/chat-memory.html&#xff09;&#xff1a;AutowiredChatMemoryRepository chatMemor…