tinypng: 一个图片压缩工具

实现一个压缩工具
更新于: 2024-12-08 09:28:32

shell

### 压缩图片
curl https://api.tinify.com/shrink \
     --user api:API_KEY \
     --data-binary @01.png

### 可运行版本
curl https://api.tinify.com/shrink \
     --user api:$TINYPNG_API_KEY \
     --data-binary @01.png

rest client

POST https://api.tinify.com/shrink
Authorization: Basic b64(api:API_KEY)
Content-Type: application/octet-stream

< ./01.png

原理说明

Authorization Header: --user api:API_KEY 被转化为 Base64 编码的 Authorization 头。
其值是 api:API_KEY 的 Base64 编码结果 b64(api:API_KEY)。
Content-Type: 指定为 application/octet-stream,用于表示二进制数据。
Body: 使用 < ./01.png 指定文件上传内容。

认证原理

这是因为 HTTP 的基本身份验证(**Basic Authentication**)机制需要使用特定格式的 `Authorization` 头。

在 Basic Authentication 中:
1. **基本格式**:`Authorization: Basic <Base64(username:password)>`  
   - 需要将 `username:password` 拼接成一个字符串。
   - 然后将拼接后的字符串用 Base64 编码。

2. **在你的例子中**:
   - `username` 是 `user`。
   - `password` 是 `PWD`。
   - 拼接后为 `api:API_KEY`。

3. **Base64 编码的作用**:
   - Base64 编码将这些凭据转换为一串 ASCII 字符,方便在 HTTP 头中传递,并避免特殊字符冲突。
   - 你的 `Authorization` 头最终就是:
     ```
     Authorization: Basic b64(user:pwd)
     ```

4. **工作原理**:
   - 服务器接收到请求后,会将 `Authorization` 头解码,提取出 `username` 和 `password`,然后进行验证。

这是一种常见的身份验证方式,虽然易于实现,但由于 Base64 并非加密,仅是编码,所以需要通过 HTTPS 确保传输安全。

golang 实现

一个调用 api 的基本实现

package main

import (
	"bytes"
	"encoding/base64"
	"fmt"
	"io"
	"net/http"
	"os"
)

func main() {
	// API URL
	url := "https://api.tinify.com/shrink"

	// API Key
	apiKey := "API_KEY"

	// 文件路径
	filePath := "01.png"

	// 读取文件内容
	fileData, err := os.ReadFile(filePath)
	if err != nil {
		fmt.Printf("Error reading file: %v\n", err)
		return
	}

	// 创建 Basic Authentication 头
	auth := "api:" + apiKey
	authHeader := base64.StdEncoding.EncodeToString([]byte(auth))

	// 创建 HTTP 请求
	req, err := http.NewRequest("POST", url, bytes.NewReader(fileData))
	if err != nil {
		fmt.Printf("Error creating request: %v\n", err)
		return
	}

	// 设置请求头
	req.Header.Set("Authorization", "Basic "+authHeader)
	req.Header.Set("Content-Type", "application/octet-stream")

	// 发送请求
	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		fmt.Printf("Error sending request: %v\n", err)
		return
	}
	defer resp.Body.Close()

	// 处理响应
	if resp.StatusCode == http.StatusCreated {
		fmt.Println("File compressed successfully!")
		respData, _ := io.ReadAll(resp.Body)
		fmt.Printf("Response: %s\n", string(respData))
	} else {
		respData, _ := io.ReadAll(resp.Body)
		fmt.Printf("Failed to compress file. Status: %d, Response: %s\n", resp.StatusCode, string(respData))
	}
}

POST url

curl https://api.tinify.com/shrink \
     --user api:$TINYPNG_API_KEY \
     --header "Content-Type: application/json" \
     --data '{"source":{"url":"https://tinypng.com/images/panda-happy.png"}}'