package map_service import ( "archive/zip" "context" "encoding/json" "fmt" "github.com/cloudwego/hertz/pkg/app" "github.com/cloudwego/hertz/pkg/protocol/consts" "io" "io/ioutil" "net/http" "os" "path/filepath" "pji_desktop_http/common/config" "pji_desktop_http/common/entity" "pji_desktop_http/common/util" ) // CheckMapBufConsistency 检查请求id对应的mapBuf文件夹的一致性 // @router /map/checkmapbuf [POST] func CheckMapBufConsistency(ctx context.Context, c *app.RequestContext) { var err error var req []string err = c.BindAndValidate(&req) if err != nil { c.String(consts.StatusBadRequest, err.Error()) return } fmt.Println(req) if len(req) == 0 { // 请求数据为空 c.JSON(consts.StatusBadRequest, entity.HttpResult{Status: false, Code: "", Message: "请求数据为空。"}) } else if len(req) == 1 { // 只有一条数据则直接返回 c.JSON(consts.StatusOK, entity.HttpResult{Status: true, Code: "", Message: "mapBuf文件夹数据一致。"}) } else { var firstValue int for i, id := range req { // 根据id获取对应的oss文件列表 fileList, err := getExactedMapFileById(id) // 过滤特定后缀的文件列表 fileList = filterBySuffixes(fileList, config.MapBufFiltersuffixes...) //fmt.Println("Filtered Strings:", fileList) if err != nil { c.String(consts.StatusBadRequest, err.Error()) return } // 获取文件列表的总大小 totalSize := calculateTotalFileSize(fileList) fmt.Println("Total Size:", totalSize) // 判断不同文件列表(mapBuf)中文件的总大小是否一致 if i == 0 { firstValue = totalSize } else { if totalSize != firstValue { c.JSON(consts.StatusOK, entity.HttpResult{Status: false, Code: "", Message: "mapBuf文件夹数据不一致。"}) return } } } c.JSON(consts.StatusOK, entity.HttpResult{Status: true, Code: "", Message: "mapBuf文件夹数据一致。"}) } } // DownloadOSSFile 根据objectKey下载指定的oss文件 // @router /map/downloadossfile [GET] func DownloadOSSFile(ctx context.Context, c *app.RequestContext) { objectKey := c.Query("objectKey") // 从OSS下载文件 reader, err := config.OssBucket.GetObject(objectKey) if err != nil { c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()}) return } defer reader.Close() // 设置响应头 c.Response.Header.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, filepath.Base(objectKey))) c.Response.Header.Set("Content-Type", "binary/octet-stream") // 将文件流式传输回客户端 data, err := io.ReadAll(reader) if err != nil { panic(err) } if _, err := c.Write(data); err != nil { c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()}) return } } // DownLoadZipFile 根据请求id从oss拉取并打包下载建图所需要的文件 // @router /map/downloadmapzipfile [GET] func DownLoadMapZipFile(ctx context.Context, c *app.RequestContext) { id := c.Query("id") fmt.Println(id) // 根据id生成用于地图更新的压缩包 filePath, tmpDir, err := generateZipById(id) if err != nil { c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()}) } fmt.Println("filePath", filePath) // 检查文件是否存在 if _, err := os.Stat(filePath); os.IsNotExist(err) { c.JSON(http.StatusNotFound, map[string]string{"error": "File not found"}) return } // 打开文件 f, err := os.Open(filePath) if err != nil { c.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to open file"}) return } defer f.Close() // 设置响应头 c.Response.Header.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, filepath.Base(filePath))) c.Response.Header.Set("Content-Type", "binary/octet-stream") // 将文件流式传输回客户端 data, err := io.ReadAll(f) if err != nil { panic(err) } if _, err := c.Write(data); err != nil { c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()}) return } defer os.RemoveAll(tmpDir) } // DownLoadMapBagFile 根据请求id从oss拉取并下载解析后的map.bag文件 // @router /map/downloadmapbagfile [GET] func DownLoadMapBagFile(ctx context.Context, c *app.RequestContext) { id := c.Query("id") fmt.Println("id: ", id) // 根据id获取对应的oss文件列表 fileList, err := getExactedMapFileById(id) // 过滤特定后缀的文件列表 fileList = filterBySuffixes(fileList, "map.bag") //fmt.Println("fileList", fileList) objectKey := fileList[0] // 从OSS下载文件 reader, err := config.OssBucket.GetObject(objectKey) if err != nil { c.JSON(consts.StatusInternalServerError, entity.HttpResult{Status: false, Code: "", Message: "解析地图Bag下载失败。"}) return } defer reader.Close() // 设置响应头 c.Response.Header.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, filepath.Base(objectKey))) c.Response.Header.Set("Content-Type", "binary/octet-stream") // 将文件流式传输回客户端 data, err := io.ReadAll(reader) if err != nil { panic(err) } if _, err := c.Write(data); err != nil { c.JSON(consts.StatusInternalServerError, entity.HttpResult{Status: false, Code: "", Message: "解析地图Bag下载失败。"}) return } c.JSON(consts.StatusOK, entity.HttpResult{Status: true, Code: "", Message: "解析地图Bag下载成功。"}) } // 根据id获取解析后的地图文件列表 func getExactedMapFileById(id string) (fileList []string, err error) { url := config.SenceOssDownUrl + id // 构建请求 req, err := http.NewRequest("GET", url, nil) if err != nil { fmt.Println("Error creating request:", err) return nil, err } // 添加认证头 req.Header.Set("Authorization", config.Token) // 发送请求 client := &http.Client{} resp, err := client.Do(req) if err != nil { fmt.Println("Error executing request:", err) return nil, err } defer resp.Body.Close() body, _ := ioutil.ReadAll(resp.Body) // Json转换为map var data map[string]interface{} err = json.Unmarshal(body, &data) // 提取响应体中的data字段 dataField, ok := data["data"].([]interface{}) if !ok { fmt.Println("Error extracting data field") return nil, err } // 转换字符串切片 for _, item := range dataField { str, ok := item.(string) if !ok { fmt.Println("Error converting item to string") return nil, err } fileList = append(fileList, str) } return fileList, nil } // 根据id获取对应的原始bag文件 func getRosFileById(id string) (file string, err error) { url := config.SenceInfoUrl + id // 构建请求 req, err := http.NewRequest("GET", url, nil) if err != nil { fmt.Println("Error creating request:", err) return "", err } // 添加认证头 req.Header.Set("Authorization", config.Token) // 发送请求 client := &http.Client{} resp, err := client.Do(req) if err != nil { fmt.Println("Error executing request:", err) return "", err } defer resp.Body.Close() body, _ := ioutil.ReadAll(resp.Body) // Json转换为map var data map[string]interface{} err = json.Unmarshal(body, &data) //fmt.Println("data:", data) // 提取响应体中的data字段 dataField, ok := data["data"].(map[string]interface{}) if !ok { fmt.Println("Error extracting data field") return "", err } // 从 data字段中提取 rosField rosField, ok := dataField["rosFileId"].(string) if !ok { fmt.Println("ID not found or not a string") return } if !ok { fmt.Println("Error extracting rosFileId field") return "", err } //println("rosFileId:", rosField) return rosField, nil } //// 根据id生成用于地图更新的压缩包 //func generateZipById(id string) (file string, tmpDir string, err error) { // // 根据id获取对应的oss文件列表 // fileList, err := getExactedMapFileById(id) // // 过滤特定后缀的文件列表 // fileList = filterBySuffixes(fileList, config.MapBufFiltersuffixes...) // //fmt.Println("Filtered Strings:", fileList) // if err != nil { // return // } // // // 创建临时文件夹 // tmpDir, err = os.MkdirTemp("", "temp-download-*") // fmt.Println("tmpDir:", tmpDir) // if err != nil { // fmt.Println("Error creating temporary directory:", err) // return "", "", err // } // // // 创建根文件夹(文件打包的根目录) // baseDir := filepath.Join(tmpDir, "data") // if err := os.Mkdir(baseDir, 0755); err != nil { // fmt.Println("Error creating subdirectory:", err) // return "", "", err // } // // 创建子文件夹 mapBuf // subDir := filepath.Join(baseDir, "mapBuf") // if err := os.Mkdir(subDir, 0755); err != nil { // fmt.Println("Error creating subdirectory:", err) // return "", "", err // } // // // 从oss下载文件到 mapBuf文件夹 // for _, file := range fileList { // err = config.OssBucket.GetObjectToFile(file, filepath.Join(subDir, filepath.Base(file))) // if err != nil { // fmt.Println("Error downloading mapBuf file:", err) // return "", "", err // } // } // // // 从oss下载bag文件到 data文件夹 // RosFileId, _ := getRosFileById(id) // err = config.OssBucket.GetObjectToFile(RosFileId, filepath.Join(baseDir, filepath.Base(RosFileId))) // if err != nil { // fmt.Println("Error downloading Bag file:", err) // return "", "", err // } // // // 创建压缩文件 // zipPath := filepath.Join(tmpDir, "mapFile-"+id+".zip") // zipFile, err := os.Create(zipPath) // if err != nil { // fmt.Println("Error creating ZIP file:", err) // return "", "", err // } // defer zipFile.Close() // // zipWriter := zip.NewWriter(zipFile) // defer zipWriter.Close() // // // 压缩文件夹 // if err := addDirToZip(baseDir, zipWriter); err != nil { // fmt.Println("Error adding directory to ZIP:", err) // return "", "", err // } // fmt.Println("ZIP file created successfully.") // // return zipPath, tmpDir, nil //} // 根据id生成用于地图更新的压缩包 func generateZipById(id string) (file string, tmpDir string, err error) { // 根据id获取对应的oss文件列表 fileList, err := getExactedMapFileById(id) // 过滤特定后缀的文件列表 fileList = filterBySuffixes(fileList, config.MapBufFiltersuffixes...) //fmt.Println("Filtered Strings:", fileList) if err != nil { return } // 创建临时文件夹 tmpDir, err = os.MkdirTemp("", "temp-download-*") fmt.Println("tmpDir:", tmpDir) if err != nil { fmt.Println("Error creating temporary directory:", err) return "", "", err } // 创建根文件夹(文件打包的根目录) baseDir := filepath.Join(tmpDir, "data") if err := os.Mkdir(baseDir, 0755); err != nil { fmt.Println("Error creating subdirectory:", err) return "", "", err } // 根目录创建子文件夹 bag_folder bagFolderDir := filepath.Join(baseDir, "bag_folder") if err := os.Mkdir(bagFolderDir, 0755); err != nil { fmt.Println("Error creating subdirectory:", err) return "", "", err } // 根目录创建子文件夹 origin_map_folder originMapFolderDir := filepath.Join(baseDir, "origin_map_folder") if err := os.Mkdir(originMapFolderDir, 0755); err != nil { fmt.Println("Error creating subdirectory:", err) return "", "", err } // 子文件夹 bag_folder 创建子文件夹 mapBuf mapBufDir := filepath.Join(bagFolderDir, "mapBuf") if err := os.Mkdir(mapBufDir, 0755); err != nil { fmt.Println("Error creating subdirectory:", err) return "", "", err } // 从oss下载bag文件到 bag_folder 文件夹 RosFileId, _ := getRosFileById(id) err = config.OssBucket.GetObjectToFile(RosFileId, filepath.Join(bagFolderDir, filepath.Base(RosFileId))) if err != nil { fmt.Println("Error downloading Bag file:", err) return "", "", err } // 从oss下载文件到 mapBuf 文件夹 for _, file := range fileList { err = config.OssBucket.GetObjectToFile(file, filepath.Join(mapBufDir, filepath.Base(file))) if err != nil { fmt.Println("Error downloading mapBuf file:", err) return "", "", err } } // 从oss下载bag文件到 origin_map_folder 文件夹 // 过滤特定后缀的文件列表 fileList = filterBySuffixes(fileList, config.OriginMapFiltersuffixes...) for _, file := range fileList { err = config.OssBucket.GetObjectToFile(file, filepath.Join(originMapFolderDir, filepath.Base(file))) if err != nil { fmt.Println("Error downloading origin_map file:", err) return "", "", err } } // 创建压缩文件 zipPath := filepath.Join(tmpDir, "mapFile-"+id+".zip") zipFile, err := os.Create(zipPath) if err != nil { fmt.Println("Error creating ZIP file:", err) return "", "", err } defer zipFile.Close() zipWriter := zip.NewWriter(zipFile) defer zipWriter.Close() // 压缩文件夹 if err := addDirToZip(baseDir, zipWriter); err != nil { fmt.Println("Error adding directory to ZIP:", err) return "", "", err } fmt.Println("ZIP file created successfully.") return zipPath, tmpDir, nil } // 从字符串切片中筛选出以特定后缀结尾的字符串 func filterBySuffixes(strList []string, suffixes ...string) []string { var filtered []string for _, s := range strList { for _, suffix := range suffixes { //if strings.HasSuffix(s, suffix) { // filtered = append(filtered, s) // break //} if filepath.Base(s) == suffix { filtered = append(filtered, s) break } } } return filtered } // 计算oss中文件列表的总大小 func calculateTotalFileSize(fileList []string) int { var totalSize int for _, file := range fileList { size, err := util.GetOSSFileSize(config.OssBucket, file) // 获取oss中单个文件的大小 if err != nil { return 0 } totalSize += size } return totalSize } // 压缩文件夹 func addDirToZip(dirPath string, zipWriter *zip.Writer) error { filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error { if err != nil { return err } if info.IsDir() { return nil } file, err := os.Open(path) if err != nil { return err } defer file.Close() // 处理路径,确保保留相对路径结构 relPath, err := filepath.Rel(dirPath, path) if err != nil { return err } header, err := zip.FileInfoHeader(info) if err != nil { return err } header.Name = filepath.ToSlash(relPath) writer, err := zipWriter.CreateHeader(header) if err != nil { return err } if _, err := io.Copy(writer, file); err != nil { return err } return nil }) return nil }