很多时候,我们需要一个临时的粘贴板,有时候我们可以使用聊天工具作为粘贴板,或者在互联网上找到类似的服务做粘贴使用. 不过这么做显然是有很多限制的,除了不够 geek 之外,有很多场合,我们需要这个粘贴板可以和其他 unix 类工具配合使用,组成更复杂的一些脚本。
那么可不可以做一个基于 curl 的粘贴板工具呢,临时粘贴的内容也比较好处理,存在对象存储里面就好了,这里我们就用腾讯云上的 cos 存储做一个小的工具【cos 的免费额度应该就够我们使用了】
首先这个服务是一个 http 服务,他需要有以下的功能:
这是一个很简单的工具,实现的代码不超过 200 行
var (
DefaultTTL = flag.Duration("default_ttl", time.Hour*24*7, "default ttl for object")
RateLimit = flag.Int("rate_limit", 1, "rate limit for api call")
SizeLimit = flag.Int64("size_limit", 1024*1024*10, "size limit for object in byte")
OSSecret = flag.String("os_secret", "::", "secret key for object storage, format: key:id:session")
BucketUrl = flag.String("bucket_url", "", "bucket_url for object storage")
NameLength = flag.Int("name_length", 4, "name length for object put")
)
func main() {
flag.Parse()
rand.Seed(time.Now().UnixNano())
key, secret, token := "", "", ""
if os.Getenv("os_secret") != "" {
*OSSecret = os.Getenv("os_secret")
}
if os.Getenv("bucket_url") != "" {
*BucketUrl = os.Getenv("bucket_url")
}
secretList := strings.Split(*OSSecret, ":")
if len(secretList) >= 1 {
key = secretList[0]
}
if len(secretList) >= 2 {
secret = secretList[1]
}
if len(secretList) >= 3 {
token = secretList[2]
}
u, err := url.Parse(*BucketUrl)
if err != nil {
panic("bucket url not valid")
}
client := cos.NewClient(&cos.BaseURL{BucketURL: u}, &http.Client{
Transport: &cos.AuthorizationTransport{
SecretID: key,
SecretKey: secret,
SessionToken: token,
},
})
go expireJob(client)
s := &http.Server{
Addr: ":80",
ReadTimeout: 60 * time.Second,
WriteTimeout: 60 * time.Second,
MaxHeaderBytes: 1 << 10,
}
limiter := rate.NewLimiter(rate.Limit(*RateLimit), *RateLimit)
http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()
_ = limiter.Wait(ctx)
if request.Method == "POST" {
log.Printf("[Handle] Method=Post Content-Length=%d", request.ContentLength)
if *SizeLimit > 0 && request.ContentLength > *SizeLimit {
writer.WriteHeader(400)
_, _ = writer.Write([]byte(fmt.Sprintf("content size out of limit: %d", *SizeLimit)))
return
}
name := fmt.Sprintf("%s/%s", time.Now().Format("20060102"), randName())
resp, err := client.Object.Put(ctx, name, request.Body, &cos.ObjectPutOptions{
ObjectPutHeaderOptions: &cos.ObjectPutHeaderOptions{},
})
if err != nil {
if resp != nil {
writer.WriteHeader(resp.StatusCode)
} else {
writer.WriteHeader(400)
}
log.Printf("[Handle] put file failed: %s", err)
_, _ = writer.Write([]byte("put file failed"))
return
}
writer.WriteHeader(resp.StatusCode)
_, _ = writer.Write([]byte(encodeName(name)))
} else if request.Method == "GET" {
name, err := decodeName(strings.Trim(request.URL.Path, "/"))
log.Printf("[Handle] Method=Get Name=%s", name)
if err != nil {
writer.WriteHeader(400)
log.Printf("[Handle] file not valid: %s", err)
_, _ = writer.Write([]byte("file name not valid"))
return
}
resp, err := client.Object.Get(ctx, name, nil)
if err != nil {
if resp != nil {
writer.WriteHeader(resp.StatusCode)
} else {
writer.WriteHeader(400)
}
log.Printf("[Handle] get file failed: %s", err)
_, _ = writer.Write([]byte("get file failed"))
return
}
data, _ := ioutil.ReadAll(resp.Body)
_, _ = writer.Write(data)
}
})
log.Println("starting server...")
log.Fatal(s.ListenAndServe())
}
func expireJob(client *cos.Client) {
for range time.Tick(time.Minute * 10) {
log.Println("do expire....")
expireTo := time.Now().Add(-1 * *DefaultTTL)
var cleaned []string
for day := 1; day <= 30; day++ {
toDelDay := expireTo.Add(time.Duration(day) * time.Hour * 24 * -1)
name := toDelDay.Format("20060102") + "/"
ctx := context.Background()
resp, err := client.Object.Head(ctx, name, nil)
if err != nil {
if resp != nil && resp.StatusCode == 404 {
continue
}
log.Println("head object failed, ", err)
continue
}
if resp.StatusCode != 200 {
continue
}
resp, err = client.Object.Delete(ctx, name, nil)
if err != nil {
log.Println("delete object failed, ", err)
continue
}
if resp.StatusCode != 200 {
log.Println("delete object failed, ", resp.Status)
} else {
cleaned = append(cleaned, name)
log.Printf("expire old folder: %s success", name)
}
}
log.Println("do expire done:", cleaned)
}
}
使用 docker 部署,dockerfile 如下
FROM alpine
ADD bin/clipboard /usr/local/bin
ENTRYPOINT ["clipboard"]
# 例子 1
➜ curl 148.70.103.38:30744 -d "hello"
20210925Vd4h%
➜ curl 148.70.103.38:30744/20210925Vd4h
hello%
# 例子 2
➜ curl 148.70.103.38:30744 -d '{"someJsonData"}'
20210925aHiw%
➜ curl 148.70.103.38:30744/20210925aHiw
{"someJsonData"}%
# 例子3: 上传一张图片
➜ curl 148.70.103.38:30744 --data-binary @`pwd`/test.png
20210925fzul%
➜ curl 148.70.103.38:30744/20210925fzul > test1.png
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 70858 0 70858 0 0 162k 0 --:--:-- --:--:-- --:--:-- 162k
完整的项目代码在 这里
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。