package main import ( "Pixel/config" "Pixel/database" "crypto/md5" "encoding/json" "fmt" "html/template" "image" "io" "log" "math/rand" "net/http" "os" "path" "path/filepath" "strconv" "time" "github.com/disintegration/imaging" ) func init() { const appinfo string = ` ______ ________ ______ __ __ __ /_____/\ /_______/\ /_____/\ /__/\ /__/\ /_/\ \:::_ \ \ \__.::._\/ \::::_\/_ \ \::\\:.\ \ \:\ \ \:(_) \ \ \::\ \ \:\/___/\ \_\::_\:_\/ \:\ \ \: ___\/ _\::\ \__ \::___\/_ _\/__\_\_/\ \:\ \____ \ \ \ /__\::\__/\ \:\____/\ \ \ \ \::\ \ \:\/___/\ \_\/ \________\/ \_____\/ \_\/ \__\/ \_____\/ ` fmt.Println(appinfo) dirPath := "./data/img" // 使用 os.Stat 检查目录是否存在 _, err := os.Stat(dirPath) if os.IsNotExist(err) { // 目录不存在,可以调用 os.Mkdir 创建 err := os.MkdirAll(dirPath, 0755) if err != nil { fmt.Println("无法创建目录:", err) return } fmt.Println("目录创建成功:", dirPath) } else if err == nil { // 目录已存在 fmt.Println("目录已存在:", dirPath) } else { // 发生其他错误 fmt.Println("发生错误:", err) } database.Initdb() //初始化数据库 fmt.Println("数据库初始化完成") } func main() { filePath := "user.json" // 读取配置文件 config, err := config.ReadConfig(filePath) if err != nil { fmt.Println("读取配置文件失败:", err) } http.HandleFunc("/info", showimg) http.HandleFunc("/info/list", showlist) // http.HandleFunc("/upload", upload) //上传图片 http.HandleFunc("/img/", downloadHandler) //图片接口 http.HandleFunc("/img/mini", displayThumbnailHandler) //缩略图接口 http.HandleFunc("/idlist", arrayHandler) //获取现有图片id http.HandleFunc("/img/del", deleteImagesHandler) //删除相应图片 http.HandleFunc("/login", login) //登录页 fmt.Println("Web服务器已启动") err = http.ListenAndServe(config.Listen, nil) //设置监听的端口 if err != nil { log.Fatal("ListenAndServe: ", err) } } func showimg(w http.ResponseWriter, r *http.Request) { t, _ := template.ParseFiles("Web/info.html") t.Execute(w, "Hello") } func showlist(w http.ResponseWriter, r *http.Request) { cookie, _ := r.Cookie("login") if cookie == nil { //未授权禁止访问 w.WriteHeader(401) w.Write([]byte(`验证失败 点此登录`)) return } t, _ := template.ParseFiles("Web/list.html") t.Execute(w, "Hello") } // 处理/upload 逻辑 func upload(w http.ResponseWriter, r *http.Request) { fmt.Println("method:", r.Method) // 获取请求的方法 if r.Method == "GET" { // 前端页面渲染 crutime := time.Now().Unix() h := md5.New() io.WriteString(h, strconv.FormatInt(crutime, 10)) token := fmt.Sprintf("%x", h.Sum(nil)) t, _ := template.ParseFiles("Web/upload.html") t.Execute(w, token) } else { // 后端POST接收逻辑 r.ParseMultipartForm(32 << 20) file, handler, err := r.FormFile("file") if err != nil { fmt.Println(err) return } defer file.Close() // 生成文件的MD5哈希 h := md5.New() if _, err := io.Copy(h, file); err != nil { fmt.Println(err) return } md5sum := fmt.Sprintf("%x", h.Sum(nil)) oldLinkID := database.GetFileLinkID(md5sum) if oldLinkID != "" { w.Write([]byte(oldLinkID)) return } // 获取文件扩展名 fname := handler.Filename ext := path.Ext(fname) // 创建新文件,使用MD5哈希和原始扩展名 newFilename := md5sum + ext f, err := os.OpenFile("./data/img/"+newFilename, os.O_WRONLY|os.O_CREATE, 0666) if err != nil { fmt.Println(err) return } defer f.Close() // 将文件内容拷贝到新文件 _, err = file.Seek(0, 0) if err != nil { fmt.Println(err) return } io.Copy(f, file) // 存入数据库 var linkid = RandomString(10) database.NewFile(linkid, md5sum, ext) w.Write([]byte(linkid)) } } func RandomString(n int) string { const letters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" bytes := make([]byte, n) rand.Seed(time.Now().UnixNano()) for i := 0; i < n; i++ { bytes[i] = letters[rand.Intn(len(letters))] } return string(bytes) } func downloadHandler(w http.ResponseWriter, r *http.Request) { // 获取请求参数,例如文件名 filename := r.FormValue("id") if filename == "" { http.Error(w, "未提供文件名", http.StatusBadRequest) return } // 拼接文件路径,确保路径安全性 filePath := filepath.Join("./data/img", database.GetFileName(filename)) // 打开文件 file, err := os.Open(filePath) if err != nil { http.Error(w, "文件未找到", http.StatusNotFound) return } defer file.Close() // 获取文件信息 fileInfo, err := file.Stat() if err != nil { http.Error(w, "无法获取文件信息", http.StatusInternalServerError) return } // 设置响应头,告诉浏览器直接显示文件 w.Header().Set("Content-Disposition", "inline; filename="+filename) w.Header().Set("Content-Type", "image/jpeg") // 适用于 JPEG 图片,根据实际文件类型设置 // 将文件内容拷贝到响应体中 http.ServeContent(w, r, filename, fileInfo.ModTime(), file) } // 辅助函数,获取文件大小 func fileSize(file *os.File) int64 { stat, err := file.Stat() if err != nil { return 0 } return stat.Size() } func generateThumbnail(filePath string, width, height int) (*image.NRGBA, error) { // 打开图像文件 img, err := imaging.Open(filePath) if err != nil { return nil, err } // 调整图像大小以创建缩略图 thumbnail := imaging.Resize(img, width, height, imaging.Lanczos) return thumbnail, nil } func displayThumbnailHandler(w http.ResponseWriter, r *http.Request) { // 获取请求参数,例如文件名 filename := r.FormValue("id") imgsize, err := strconv.Atoi(r.FormValue("size")) if err != nil { http.Error(w, "size参数错误 应为纯数字", http.StatusBadRequest) } if imgsize <= 1 || imgsize > 1024 { http.Error(w, "size参数错误 范围应在0-1024之间", http.StatusBadRequest) } if filename == "" { http.Error(w, "未提供文件名", http.StatusBadRequest) return } // 拼接文件路径,确保路径安全性 filePath := filepath.Join("./data/img", database.GetFileName(filename)) // 生成缩略图 thumbnail, err := generateThumbnail(filePath, imgsize, 0) // 设置缩略图的宽度和高度 if err != nil { http.Error(w, "无法生成缩略图", http.StatusInternalServerError) return } // 设置响应头,指示这是一张图像 w.Header().Set("Content-Type", "image/jpeg") // 使用 JPEG 格式或根据需要调整 // 将缩略图写入响应体 if err := imaging.Encode(w, thumbnail, imaging.JPEG); err != nil { http.Error(w, "无法编码图像", http.StatusInternalServerError) return } } func arrayHandler(w http.ResponseWriter, r *http.Request) { //获取全部图片ID cookie, _ := r.Cookie("login") if cookie == nil { //未授权禁止访问 w.WriteHeader(401) w.Write([]byte(`验证失败 点此登录`)) return } // 获取数组数据 data, err := database.QueryId() if err != nil { http.Error(w, "数组数据获取失败", http.StatusInternalServerError) return } // 将数组数据转换为 JSON 格式 responseData, err := json.Marshal(data) if err != nil { http.Error(w, "无法处理数组数据", http.StatusInternalServerError) return } // 设置响应头,指定内容类型为 JSON w.Header().Set("Content-Type", "application/json") // 将 JSON 数据写入响应体 w.Write(responseData) } func deleteImagesHandler(w http.ResponseWriter, r *http.Request) { cookie, _ := r.Cookie("login") if cookie == nil { //未授权禁止访问 w.WriteHeader(401) w.Write([]byte(`验证失败 点此登录`)) return } // 从请求参数中获取目录名 id := r.FormValue("id") if id == "" { http.Error(w, "未提供id", http.StatusBadRequest) return } // 拼接目录路径,确保路径安全性 dirPath := filepath.Join("./data/img/", database.GetFileName(id)) // 删除目录及其所有内容 if err := os.Remove(dirPath); err != nil { http.Error(w, fmt.Sprintf("无法删除 %s: %s", database.GetFileName(id), err), http.StatusInternalServerError) return } database.DelFile(id) //删除数据库相关记录 // 返回成功的响应 w.WriteHeader(http.StatusOK) w.Write([]byte("成功删除")) } func login(w http.ResponseWriter, r *http.Request) { r.ParseForm() if r.Method == "GET" { t, _ := template.ParseFiles("Web/login.html") w.Header().Set("Content-Type", "text/html") t.Execute(w, "") } else { userlist, _ := database.QueryUser() fmt.Println(userlist) if len(userlist) == 0 { database.NewUser("admin", r.FormValue("passwd")) } else { if !database.CheckUserPasswd("admin", r.FormValue("passwd")) { http.Redirect(w, r, "/login", http.StatusFound) fmt.Println("密码错误") return } } cookie := http.Cookie{Name: "login", Value: "yes"} http.SetCookie(w, &cookie) fmt.Println("密码正确") http.Redirect(w, r, "/info/list", http.StatusFound) } }