test.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765
  1. package controllers
  2. import (
  3. "fmt"
  4. "image"
  5. "image/jpeg"
  6. "image/png"
  7. "io"
  8. "net/http"
  9. "os"
  10. "path/filepath"
  11. "regexp"
  12. "strconv"
  13. "strings"
  14. "time"
  15. "shudao-chat-go/models"
  16. "github.com/aws/aws-sdk-go/aws"
  17. "github.com/aws/aws-sdk-go/aws/credentials"
  18. "github.com/aws/aws-sdk-go/aws/session"
  19. "github.com/aws/aws-sdk-go/service/s3"
  20. )
  21. // OSS配置信息
  22. var (
  23. testOssBucket = "gdsc-ai-aqzs"
  24. testOssAccessKey = "fnyfi2f368pbic74d8ll"
  25. testOssSecretKey = "jgqwk7sirqlz2602x2k7yx2eor0vii19wah6ywlv"
  26. testOssEndpoint = "http://172.16.17.52:8060"
  27. testOssRegion = "us-east-1"
  28. )
  29. // 批量上传文件到OSS
  30. func BatchUploadFilesToOSS() {
  31. fmt.Println("=== 开始批量上传文件到OSS ===")
  32. // 设置文件文件夹路径
  33. fileFolder := "C:/Users/allen/Desktop/分类文件/办法"
  34. fmt.Printf("目标文件夹: %s\n", fileFolder)
  35. // 检查文件夹是否存在
  36. if _, err := os.Stat(fileFolder); os.IsNotExist(err) {
  37. fmt.Printf("错误: 文件夹不存在: %s\n", fileFolder)
  38. return
  39. }
  40. fmt.Println("文件夹存在,开始扫描...")
  41. // 获取所有文件
  42. files, err := getDocumentFiles(fileFolder)
  43. if err != nil {
  44. fmt.Printf("错误: 获取文件失败: %v\n", err)
  45. return
  46. }
  47. fmt.Printf("找到 %d 个文件\n", len(files))
  48. // 创建S3会话
  49. sess, err := createS3Session()
  50. if err != nil {
  51. fmt.Printf("错误: 创建S3会话失败: %v\n", err)
  52. return
  53. }
  54. s3Client := s3.New(sess)
  55. // 处理每个文件
  56. successCount := 0
  57. errorCount := 0
  58. for i, filePath := range files {
  59. fmt.Printf("处理文件 %d/%d: %s\n", i+1, len(files), filepath.Base(filePath))
  60. // 获取文件名(不含路径)
  61. fileName := filepath.Base(filePath)
  62. // 上传文件到OSS
  63. fileURL, err := uploadFileToOSSTest(s3Client, filePath, fileName)
  64. if err != nil {
  65. fmt.Printf(" 错误: 上传文件失败 - %v\n", err)
  66. errorCount++
  67. continue
  68. }
  69. fmt.Printf(" 文件上传成功: %s\n", fileURL)
  70. //去除Filename点后面的内容
  71. fileName = strings.TrimSuffix(fileName, filepath.Ext(fileName))
  72. //去掉fileName前面“(内部)”这两个字
  73. fileName = strings.TrimPrefix(fileName, "(内部)")
  74. // 保存到index_file表
  75. indexFile := models.PolicyFile{
  76. PolicyName: fileName,
  77. PolicyFileUrl: fileURL,
  78. FileType: 0,
  79. FileTag: "内部法规",
  80. PublishTime: time.Now().Unix(),
  81. PolicyType: 4,
  82. PolicyDepartment: "内部法规",
  83. ViewCount: 0,
  84. PolicyContent: "内部法规",
  85. }
  86. result := models.DB.Create(&indexFile)
  87. if result.Error != nil {
  88. fmt.Printf(" 错误: 保存到数据库失败 - %v\n", result.Error)
  89. errorCount++
  90. continue
  91. }
  92. fmt.Printf(" 数据库保存成功,ID: %d\n", indexFile.ID)
  93. successCount++
  94. // 添加延迟避免请求过于频繁
  95. // time.Sleep(100 * time.Millisecond)
  96. }
  97. fmt.Println("处理完成!")
  98. fmt.Printf("成功处理: %d 个文件\n", successCount)
  99. fmt.Printf("处理失败: %d 个文件\n", errorCount)
  100. }
  101. // 批量匹配图片
  102. func BatchMatchImages() {
  103. fmt.Println("=== 开始批量匹配图片 ===")
  104. // 获取test表中所有有title1的记录
  105. var testRecords []models.Test
  106. result := models.DB.Where("title1 != '' AND title1 IS NOT NULL").Find(&testRecords)
  107. if result.Error != nil {
  108. fmt.Printf("错误: 获取test表记录失败: %v\n", result.Error)
  109. return
  110. }
  111. fmt.Printf("找到 %d 条test表记录\n", len(testRecords))
  112. successCount := 0
  113. errorCount := 0
  114. for i, testRecord := range testRecords {
  115. fmt.Printf("处理记录 %d/%d: ID=%d, Title1=%s\n", i+1, len(testRecords), testRecord.ID, testRecord.Title1)
  116. // 根据title1在third_scene表中查找匹配的记录
  117. var thirdScene models.ThirdScene
  118. result := models.DB.Where("third_scene_name = ?", testRecord.Title1).First(&thirdScene)
  119. if result.Error != nil {
  120. fmt.Printf(" 错误: 在third_scene表中未找到匹配记录 - %v\n", result.Error)
  121. errorCount++
  122. continue
  123. }
  124. fmt.Printf(" 找到匹配的third_scene记录: ID=%d, Name=%s\n", thirdScene.ID, thirdScene.ThirdSceneName)
  125. // 更新third_scene表的correct_example_image和wrong_example_image
  126. updateData := map[string]interface{}{
  127. "correct_example_image": testRecord.Title2,
  128. "wrong_example_image": testRecord.Title3,
  129. }
  130. result = models.DB.Model(&thirdScene).Updates(updateData)
  131. if result.Error != nil {
  132. fmt.Printf(" 错误: 更新third_scene表失败 - %v\n", result.Error)
  133. errorCount++
  134. continue
  135. }
  136. fmt.Printf(" 更新成功: correct_example_image=%s, wrong_example_image=%s\n", testRecord.Title2, testRecord.Title3)
  137. successCount++
  138. }
  139. fmt.Println("匹配完成!")
  140. fmt.Printf("成功匹配: %d 条记录\n", successCount)
  141. fmt.Printf("匹配失败: %d 条记录\n", errorCount)
  142. }
  143. // BatchUploadImages 批量上传图片函数
  144. func BatchUploadImages() {
  145. fmt.Println("=== 开始批量上传图片 ===")
  146. // 设置图片文件夹路径
  147. imageFolder := "C:/Users/allen/Desktop/隐患识别图/错误图片"
  148. fmt.Printf("目标文件夹: %s\n", imageFolder)
  149. // 检查文件夹是否存在
  150. if _, err := os.Stat(imageFolder); os.IsNotExist(err) {
  151. fmt.Printf("错误: 文件夹不存在: %s\n", imageFolder)
  152. return
  153. }
  154. fmt.Println("文件夹存在,开始扫描...")
  155. // 获取所有图片文件
  156. imageFiles, err := getImageFiles(imageFolder)
  157. if err != nil {
  158. fmt.Printf("错误: 获取图片文件失败: %v\n", err)
  159. return
  160. }
  161. fmt.Printf("找到 %d 个图片文件\n", len(imageFiles))
  162. // 创建S3会话
  163. sess, err := createS3Session()
  164. if err != nil {
  165. fmt.Printf("错误: 创建S3会话失败: %v\n", err)
  166. return
  167. }
  168. s3Client := s3.New(sess)
  169. // 处理每个图片文件(只处理前10个文件进行测试)
  170. successCount := 0
  171. errorCount := 0
  172. // 限制处理文件数量
  173. for i, imageFile := range imageFiles {
  174. fmt.Printf("处理文件 %d/%d: %s\n", i+1, len(imageFiles), filepath.Base(imageFile))
  175. // 提取文件名中的序号
  176. fileID, err := extractIDFromFilename(filepath.Base(imageFile))
  177. if err != nil {
  178. fmt.Printf(" 错误: 无法提取序号 - %v\n", err)
  179. errorCount++
  180. continue
  181. }
  182. fmt.Printf(" 提取的序号: %d\n", fileID)
  183. // 检查数据库中是否存在对应的记录
  184. var testRecord models.Test
  185. result := models.DB.First(&testRecord, fileID)
  186. if result.Error != nil {
  187. fmt.Printf(" 错误: 数据库中未找到ID为 %d 的记录 - %v\n", fileID, result.Error)
  188. errorCount++
  189. continue
  190. }
  191. fmt.Printf(" 找到数据库记录: ID=%d, Title1=%s\n", testRecord.ID, testRecord.Title1)
  192. // 上传图片到OSS
  193. imageURL, err := uploadImageToOSSTest(s3Client, imageFile, fileID)
  194. if err != nil {
  195. fmt.Printf(" 错误: 上传图片失败 - %v\n", err)
  196. errorCount++
  197. continue
  198. }
  199. fmt.Printf(" 图片上传成功: %s\n", imageURL)
  200. // 更新数据库记录
  201. result = models.DB.Model(&testRecord).Update("title3", imageURL)
  202. if result.Error != nil {
  203. fmt.Printf(" 错误: 更新数据库失败 - %v\n", result.Error)
  204. errorCount++
  205. continue
  206. }
  207. fmt.Printf(" 数据库更新成功\n")
  208. successCount++
  209. // 添加延迟避免请求过于频繁
  210. time.Sleep(100 * time.Millisecond)
  211. }
  212. fmt.Println("处理完成!")
  213. fmt.Printf("成功处理: %d 个文件\n", successCount)
  214. fmt.Printf("处理失败: %d 个文件\n", errorCount)
  215. }
  216. // 获取文件夹中的所有文档文件
  217. func getDocumentFiles(folderPath string) ([]string, error) {
  218. var documentFiles []string
  219. // 支持的文档格式
  220. documentExts := map[string]bool{
  221. ".pdf": true,
  222. ".doc": true,
  223. ".docx": true,
  224. ".txt": true,
  225. ".rtf": true,
  226. ".odt": true,
  227. ".xls": true,
  228. ".xlsx": true,
  229. ".ppt": true,
  230. ".pptx": true,
  231. }
  232. err := filepath.Walk(folderPath, func(path string, info os.FileInfo, err error) error {
  233. if err != nil {
  234. return err
  235. }
  236. if !info.IsDir() {
  237. ext := strings.ToLower(filepath.Ext(path))
  238. if documentExts[ext] {
  239. documentFiles = append(documentFiles, path)
  240. }
  241. }
  242. return nil
  243. })
  244. return documentFiles, err
  245. }
  246. // 获取文件夹中的所有图片文件
  247. func getImageFiles(folderPath string) ([]string, error) {
  248. var imageFiles []string
  249. // 支持的图片格式
  250. imageExts := map[string]bool{
  251. ".jpg": true,
  252. ".jpeg": true,
  253. ".png": true,
  254. ".gif": true,
  255. ".bmp": true,
  256. ".webp": true,
  257. ".tiff": true,
  258. ".svg": true,
  259. ".ico": true,
  260. }
  261. err := filepath.Walk(folderPath, func(path string, info os.FileInfo, err error) error {
  262. if err != nil {
  263. return err
  264. }
  265. if !info.IsDir() {
  266. ext := strings.ToLower(filepath.Ext(path))
  267. if imageExts[ext] {
  268. imageFiles = append(imageFiles, path)
  269. }
  270. }
  271. return nil
  272. })
  273. return imageFiles, err
  274. }
  275. // 从文件名中提取序号
  276. func extractIDFromFilename(filename string) (int, error) {
  277. // 移除文件扩展名
  278. nameWithoutExt := strings.TrimSuffix(filename, filepath.Ext(filename))
  279. // 使用正则表达式提取开头的数字
  280. re := regexp.MustCompile(`^(\d+)`)
  281. matches := re.FindStringSubmatch(nameWithoutExt)
  282. if len(matches) < 2 {
  283. return 0, fmt.Errorf("文件名中未找到数字序号: %s", filename)
  284. }
  285. id, err := strconv.Atoi(matches[1])
  286. if err != nil {
  287. return 0, fmt.Errorf("无法解析序号: %s", matches[1])
  288. }
  289. return id, nil
  290. }
  291. // 创建S3会话
  292. func createS3Session() (*session.Session, error) {
  293. s3Config := &aws.Config{
  294. Credentials: credentials.NewStaticCredentials(testOssAccessKey, testOssSecretKey, ""),
  295. Endpoint: aws.String(testOssEndpoint),
  296. Region: aws.String(testOssRegion),
  297. S3ForcePathStyle: aws.Bool(true),
  298. }
  299. sess, err := session.NewSession(s3Config)
  300. if err != nil {
  301. return nil, err
  302. }
  303. // 验证凭据
  304. _, err = sess.Config.Credentials.Get()
  305. if err != nil {
  306. return nil, fmt.Errorf("凭据验证失败: %v", err)
  307. }
  308. return sess, nil
  309. }
  310. // 上传文件到OSS
  311. func uploadFileToOSSTest(s3Client *s3.S3, filePath string, fileName string) (string, error) {
  312. // 打开文件
  313. file, err := os.Open(filePath)
  314. if err != nil {
  315. return "", err
  316. }
  317. defer file.Close()
  318. // 生成文件名
  319. ext := filepath.Ext(filePath)
  320. ossFileName := fmt.Sprintf("documents/%d_%s",
  321. time.Now().Unix(), fileName)
  322. // 读取文件内容
  323. fileBytes, err := io.ReadAll(file)
  324. if err != nil {
  325. return "", err
  326. }
  327. // 确定Content-Type
  328. contentType := getContentType(ext)
  329. // 上传到S3
  330. _, err = s3Client.PutObject(&s3.PutObjectInput{
  331. Bucket: aws.String(testOssBucket),
  332. Key: aws.String(ossFileName),
  333. Body: aws.ReadSeekCloser(strings.NewReader(string(fileBytes))),
  334. ContentType: aws.String(contentType),
  335. ACL: aws.String("public-read"),
  336. })
  337. if err != nil {
  338. return "", err
  339. }
  340. // 生成访问URL
  341. fileURL := fmt.Sprintf("%s/%s/%s", testOssEndpoint, testOssBucket, ossFileName)
  342. return fileURL, nil
  343. }
  344. // 根据文件扩展名获取Content-Type
  345. func getContentType(ext string) string {
  346. contentTypes := map[string]string{
  347. ".pdf": "application/pdf",
  348. ".doc": "application/msword",
  349. ".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
  350. ".txt": "text/plain",
  351. ".rtf": "application/rtf",
  352. ".odt": "application/vnd.oasis.opendocument.text",
  353. ".xls": "application/vnd.ms-excel",
  354. ".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
  355. ".ppt": "application/vnd.ms-powerpoint",
  356. ".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
  357. }
  358. if contentType, exists := contentTypes[strings.ToLower(ext)]; exists {
  359. return contentType
  360. }
  361. return "application/octet-stream" // 默认类型
  362. }
  363. // 上传图片到OSS
  364. func uploadImageToOSSTest(s3Client *s3.S3, imagePath string, fileID int) (string, error) {
  365. // 打开图片文件
  366. file, err := os.Open(imagePath)
  367. if err != nil {
  368. return "", err
  369. }
  370. defer file.Close()
  371. // 生成文件名
  372. ext := filepath.Ext(imagePath)
  373. fileName := fmt.Sprintf("batch_upload/%d_%d%s",
  374. time.Now().Unix(), fileID, ext)
  375. // 读取文件内容
  376. fileBytes, err := io.ReadAll(file)
  377. if err != nil {
  378. return "", err
  379. }
  380. // 上传到S3
  381. _, err = s3Client.PutObject(&s3.PutObjectInput{
  382. Bucket: aws.String(testOssBucket),
  383. Key: aws.String(fileName),
  384. Body: aws.ReadSeekCloser(strings.NewReader(string(fileBytes))),
  385. ACL: aws.String("public-read"),
  386. })
  387. if err != nil {
  388. return "", err
  389. }
  390. // 生成访问URL
  391. imageURL := fmt.Sprintf("%s/%s/%s", testOssEndpoint, testOssBucket, fileName)
  392. return imageURL, nil
  393. }
  394. // BatchCompressAndReuploadImages 批量压缩和重新上传third_scene表中的图片
  395. func BatchCompressAndReuploadImages() {
  396. fmt.Println("=== 开始批量压缩和重新上传图片 ===")
  397. // 获取third_scene表中所有记录
  398. var thirdScenes []models.ThirdScene
  399. result := models.DB.Find(&thirdScenes)
  400. if result.Error != nil {
  401. fmt.Printf("错误: 获取third_scene表记录失败: %v\n", result.Error)
  402. return
  403. }
  404. fmt.Printf("找到 %d 条third_scene记录\n", len(thirdScenes))
  405. // 创建S3会话
  406. sess, err := createS3Session()
  407. if err != nil {
  408. fmt.Printf("错误: 创建S3会话失败: %v\n", err)
  409. return
  410. }
  411. s3Client := s3.New(sess)
  412. successCount := 0
  413. errorCount := 0
  414. skipCount := 0
  415. for i, thirdScene := range thirdScenes {
  416. fmt.Printf("处理记录 %d/%d: ID=%d, Name=%s\n", i+1, len(thirdScenes), thirdScene.ID, thirdScene.ThirdSceneName)
  417. // 处理correct_example_image
  418. if thirdScene.CorrectExampleImage != "" {
  419. fmt.Printf(" 处理correct_example_image: %s\n", thirdScene.CorrectExampleImage)
  420. newURL, err := compressAndReuploadImage(s3Client, thirdScene.CorrectExampleImage, fmt.Sprintf("correct_%d", thirdScene.ID))
  421. if err != nil {
  422. fmt.Printf(" 错误: 处理correct_example_image失败 - %v\n", err)
  423. errorCount++
  424. } else {
  425. thirdScene.CorrectExampleImage = newURL
  426. fmt.Printf(" correct_example_image更新成功: %s\n", newURL)
  427. }
  428. } else {
  429. fmt.Printf(" correct_example_image为空,跳过\n")
  430. skipCount++
  431. }
  432. // 处理wrong_example_image
  433. if thirdScene.WrongExampleImage != "" {
  434. fmt.Printf(" 处理wrong_example_image: %s\n", thirdScene.WrongExampleImage)
  435. newURL, err := compressAndReuploadImage(s3Client, thirdScene.WrongExampleImage, fmt.Sprintf("wrong_%d", thirdScene.ID))
  436. if err != nil {
  437. fmt.Printf(" 错误: 处理wrong_example_image失败 - %v\n", err)
  438. errorCount++
  439. } else {
  440. thirdScene.WrongExampleImage = newURL
  441. fmt.Printf(" wrong_example_image更新成功: %s\n", newURL)
  442. }
  443. } else {
  444. fmt.Printf(" wrong_example_image为空,跳过\n")
  445. skipCount++
  446. }
  447. // 更新数据库记录
  448. result := models.DB.Model(&thirdScene).Updates(map[string]interface{}{
  449. "correct_example_image": thirdScene.CorrectExampleImage,
  450. "wrong_example_image": thirdScene.WrongExampleImage,
  451. })
  452. if result.Error != nil {
  453. fmt.Printf(" 错误: 更新数据库失败 - %v\n", result.Error)
  454. errorCount++
  455. } else {
  456. fmt.Printf(" 数据库更新成功\n")
  457. successCount++
  458. }
  459. // 添加延迟避免请求过于频繁
  460. time.Sleep(200 * time.Millisecond)
  461. }
  462. fmt.Println("处理完成!")
  463. fmt.Printf("成功处理: %d 条记录\n", successCount)
  464. fmt.Printf("处理失败: %d 条记录\n", errorCount)
  465. fmt.Printf("跳过空字段: %d 次\n", skipCount)
  466. }
  467. // compressAndReuploadImage 压缩并重新上传图片
  468. func compressAndReuploadImage(s3Client *s3.S3, imageURL string, prefix string) (string, error) {
  469. // 下载图片
  470. tempFilePath, err := downloadImage(imageURL)
  471. if err != nil {
  472. return "", fmt.Errorf("下载图片失败: %v", err)
  473. }
  474. defer os.Remove(tempFilePath) // 清理临时文件
  475. // 压缩图片
  476. compressedFilePath, err := compressImageFile(tempFilePath)
  477. if err != nil {
  478. return "", fmt.Errorf("压缩图片失败: %v", err)
  479. }
  480. defer os.Remove(compressedFilePath) // 清理临时文件
  481. // 上传压缩后的图片
  482. newURL, err := uploadCompressedImageToOSS(s3Client, compressedFilePath, prefix)
  483. if err != nil {
  484. return "", fmt.Errorf("上传压缩图片失败: %v", err)
  485. }
  486. return newURL, nil
  487. }
  488. // downloadImage 下载图片到临时文件
  489. func downloadImage(imageURL string) (string, error) {
  490. // 创建HTTP请求
  491. resp, err := http.Get(imageURL)
  492. if err != nil {
  493. return "", err
  494. }
  495. defer resp.Body.Close()
  496. if resp.StatusCode != http.StatusOK {
  497. return "", fmt.Errorf("下载失败,状态码: %d", resp.StatusCode)
  498. }
  499. // 创建临时文件
  500. tempFile, err := os.CreateTemp("", "download_*.jpg")
  501. if err != nil {
  502. return "", err
  503. }
  504. defer tempFile.Close()
  505. // 复制内容到临时文件
  506. _, err = io.Copy(tempFile, resp.Body)
  507. if err != nil {
  508. os.Remove(tempFile.Name())
  509. return "", err
  510. }
  511. return tempFile.Name(), nil
  512. }
  513. // compressImageFile 压缩图片到1M以下
  514. func compressImageFile(inputPath string) (string, error) {
  515. // 打开原始图片
  516. file, err := os.Open(inputPath)
  517. if err != nil {
  518. return "", err
  519. }
  520. defer file.Close()
  521. // 解码图片
  522. img, format, err := image.Decode(file)
  523. if err != nil {
  524. return "", err
  525. }
  526. // 创建临时文件用于压缩后的图片
  527. tempFile, err := os.CreateTemp("", "compressed_*.jpg")
  528. if err != nil {
  529. return "", err
  530. }
  531. defer tempFile.Close()
  532. // 尝试不同的质量级别直到文件大小小于1MB
  533. targetSize := int64(1024 * 1024) // 1MB
  534. quality := 90
  535. for quality > 10 {
  536. // 重置文件指针
  537. tempFile.Seek(0, 0)
  538. tempFile.Truncate(0)
  539. // 根据格式编码图片
  540. if format == "png" {
  541. err = png.Encode(tempFile, img)
  542. } else {
  543. err = jpeg.Encode(tempFile, img, &jpeg.Options{Quality: quality})
  544. }
  545. if err != nil {
  546. return "", err
  547. }
  548. // 检查文件大小
  549. fileInfo, err := tempFile.Stat()
  550. if err != nil {
  551. return "", err
  552. }
  553. if fileInfo.Size() <= targetSize {
  554. break
  555. }
  556. quality -= 10
  557. }
  558. // 如果还是太大,尝试缩放图片
  559. if quality <= 10 {
  560. // 获取图片尺寸
  561. bounds := img.Bounds()
  562. width := bounds.Dx()
  563. height := bounds.Dy()
  564. // 计算缩放比例
  565. scale := 0.8
  566. newWidth := int(float64(width) * scale)
  567. newHeight := int(float64(height) * scale)
  568. // 创建缩放后的图片
  569. resizedImg := resizeImageFile(img, newWidth, newHeight)
  570. // 重置文件指针
  571. tempFile.Seek(0, 0)
  572. tempFile.Truncate(0)
  573. // 编码缩放后的图片
  574. err = jpeg.Encode(tempFile, resizedImg, &jpeg.Options{Quality: 80})
  575. if err != nil {
  576. return "", err
  577. }
  578. }
  579. return tempFile.Name(), nil
  580. }
  581. // resizeImageFile 缩放图片
  582. func resizeImageFile(img image.Image, width, height int) image.Image {
  583. // 简单的最近邻缩放
  584. bounds := img.Bounds()
  585. srcWidth := bounds.Dx()
  586. srcHeight := bounds.Dy()
  587. // 创建新的图片
  588. newImg := image.NewRGBA(image.Rect(0, 0, width, height))
  589. // 计算缩放比例
  590. xRatio := float64(srcWidth) / float64(width)
  591. yRatio := float64(srcHeight) / float64(height)
  592. for y := 0; y < height; y++ {
  593. for x := 0; x < width; x++ {
  594. srcX := int(float64(x) * xRatio)
  595. srcY := int(float64(y) * yRatio)
  596. newImg.Set(x, y, img.At(srcX, srcY))
  597. }
  598. }
  599. return newImg
  600. }
  601. // uploadCompressedImageToOSS 上传压缩后的图片到OSS
  602. func uploadCompressedImageToOSS(s3Client *s3.S3, imagePath string, prefix string) (string, error) {
  603. // 打开图片文件
  604. file, err := os.Open(imagePath)
  605. if err != nil {
  606. return "", err
  607. }
  608. defer file.Close()
  609. // 生成文件名
  610. fileName := fmt.Sprintf("compressed_images/%s_%d.jpg", prefix, time.Now().Unix())
  611. // 读取文件内容
  612. fileBytes, err := io.ReadAll(file)
  613. if err != nil {
  614. return "", err
  615. }
  616. // 上传到S3
  617. _, err = s3Client.PutObject(&s3.PutObjectInput{
  618. Bucket: aws.String(testOssBucket),
  619. Key: aws.String(fileName),
  620. Body: aws.ReadSeekCloser(strings.NewReader(string(fileBytes))),
  621. ContentType: aws.String("image/jpeg"),
  622. ACL: aws.String("public-read"),
  623. })
  624. if err != nil {
  625. return "", err
  626. }
  627. // 生成访问URL
  628. imageURL := fmt.Sprintf("%s/%s/%s", testOssEndpoint, testOssBucket, fileName)
  629. return imageURL, nil
  630. }