forked from h2non/imaginary
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathdz_files.go
200 lines (165 loc) · 5.02 KB
/
dz_files.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
package main
import (
"fmt"
"io/ioutil"
"math/rand"
"os"
"os/exec"
"path/filepath"
"time"
"golang.org/x/sync/errgroup"
)
func init() {
// TODO: since we are using rand, we need this seed somewhere better
rand.Seed(time.Now().UnixNano())
}
// Make sure that all implementations implement ImageDownUploader
var (
_ ImageDownUploader = (*AzureSASSource)(nil)
_ ImageDownUploader = (*AzureImageSource)(nil)
_ ImageDownUploader = (*S3Source)(nil)
)
type ImageDownUploader interface {
DownloadImage(container, imageKey string) ([]byte, error)
UploadImage(data []byte, fileKey, container string) error
}
func initDownloadUploader(dzConf DZFilesConfig) (ImageDownUploader, error) {
switch dzConf.Provider {
case "azure":
source := NewAzureImageSource(nil).(ImageDownUploader)
return source, nil
case "s3":
return &S3Source{
Zone: dzConf.ContainerZone,
}, nil
case "azureSAS":
return &AzureSASSource{
SASToken: dzConf.SASToken,
AccountName: dzConf.AccountName,
}, nil
}
return nil, fmt.Errorf("dzfiles: unknown provider")
}
type DZFilesConfig struct {
Provider string // azure || azureSAS || s3
ImageKey string
Container string
TempContainer string
ContainerZone string // container zone (s3 region)
SASToken string // sas token for azure
AccountName string // account name which is used in conjunction with sas token
}
func UploadDZFiles(dzConf DZFilesConfig) error {
downUploader, err := initDownloadUploader(dzConf)
if err != nil {
return fmt.Errorf("dzfiles: error getting source: %w", err)
}
keyDir, imageName := filepath.Split(dzConf.ImageKey)
fileExtension := filepath.Ext(imageName)
imageName = imageName[:len(imageName)-len(fileExtension)]
if err := downUploader.UploadImage(
[]byte("pending"),
filepath.Join(keyDir, imageName+".txt"),
dzConf.TempContainer,
); err != nil {
return fmt.Errorf("dzfiles: error creating txt file: %w", err)
}
// TODO: this is just an ugly hack which is terrible, this needs to be solved with
// async task.
go func() (err error) {
// TODO: hack to defer error
defer func() {
if err != nil {
downUploader.UploadImage(
[]byte(err.Error()),
filepath.Join(keyDir, imageName+".txt"),
dzConf.TempContainer,
)
fmt.Printf("dzfiles: error: %s", err)
}
}()
localDirPath := fmt.Sprintf("/tmp/dzFiles-%d", rand.Uint64())
if err := os.Mkdir(localDirPath, 0777); err != nil {
return fmt.Errorf("dzfiles: error creating tmp dir: %w", err)
}
defer os.RemoveAll(localDirPath)
data, err := downUploader.DownloadImage(dzConf.Container, dzConf.ImageKey)
if err != nil {
return fmt.Errorf("dzfiles: error downloading image: %w", err)
}
if err := generateDZFiles(localDirPath, data, imageName, fileExtension); err != nil {
return fmt.Errorf("dzfiles: error generating dz files: %w", err)
}
var g errgroup.Group
g.Go(func() error {
data, err := ioutil.ReadFile(filepath.Join(localDirPath, imageName+".dzi"))
if err != nil {
return fmt.Errorf("dzfiles: error reading index file: %w", err)
}
if err := downUploader.UploadImage(
data,
filepath.Join(keyDir, imageName+".dzi"),
dzConf.TempContainer,
); err != nil {
return fmt.Errorf("dzfiles: error uploading index file: %w", err)
}
return nil
})
if err := filepath.Walk(
filepath.Join(localDirPath, imageName+"_files"),
filepath.WalkFunc(
func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
g.Go(func() error {
data, err := ioutil.ReadFile(path)
if err != nil {
return fmt.Errorf("dzfiles: error reading file: %s: %w", path, err)
}
if err := downUploader.UploadImage(
data,
keyDir+path[len(localDirPath)+1:], // +1 -> for slash "/",
dzConf.TempContainer,
); err != nil {
return fmt.Errorf("dzfiles: error uploading file: %s: %w", path, err)
}
return nil
})
return nil
},
),
); err != nil {
return fmt.Errorf("dzfiles: error walking dir path: %w", err)
}
if err := g.Wait(); err != nil {
return err
}
if err := downUploader.UploadImage(
[]byte("ok"),
filepath.Join(keyDir, imageName+".txt"),
dzConf.TempContainer,
); err != nil {
return fmt.Errorf("dzfiles: error creating ok txt file: %s", err)
}
fmt.Printf("DZfiles upload for: %s\n", filepath.Join(keyDir, imageName))
return nil
}()
return nil
}
// generateDZFiles generates dz files in given dir with same image name, just changed
// extension.
func generateDZFiles(dirPath string, data []byte, imageName string, fileExtension string) error {
imagePath := fmt.Sprintf("%s/%s", dirPath, imageName)
tiffImagePath := imagePath + fileExtension
if err := ioutil.WriteFile(tiffImagePath, data, 0777); err != nil {
return fmt.Errorf("dzfiles: error saving tiff image to disk: %w", err)
}
if err := exec.Command("vips", "dzsave", tiffImagePath, imagePath).Run(); err != nil {
return fmt.Errorf("dzfiles: error creating dz files: %w", err)
}
return nil
}