github.com/cozy/cozy-stack@v0.0.0-20240603063001-31110fa4cae1/worker/archive/unzip.go (about) 1 package archive 2 3 import ( 4 "archive/zip" 5 "fmt" 6 "io" 7 "path" 8 "time" 9 10 "github.com/cozy/cozy-stack/model/job" 11 "github.com/cozy/cozy-stack/model/vfs" 12 "github.com/cozy/cozy-stack/pkg/couchdb" 13 "github.com/cozy/cozy-stack/pkg/utils" 14 ) 15 16 type unzipMessage struct { 17 Zip string `json:"zip"` 18 Destination string `json:"destination"` 19 } 20 21 // WorkerUnzip is a worker that unzip a file. 22 func WorkerUnzip(ctx *job.TaskContext) error { 23 msg := &unzipMessage{} 24 if err := ctx.UnmarshalMessage(msg); err != nil { 25 return err 26 } 27 fs := ctx.Instance.VFS() 28 return unzip(fs, msg.Zip, msg.Destination) 29 } 30 31 func unzip(fs vfs.VFS, zipID, destination string) error { 32 zipDoc, err := fs.FileByID(zipID) 33 if err != nil { 34 return err 35 } 36 dstDoc, err := fs.DirByID(destination) 37 if err != nil { 38 return err 39 } 40 41 fr, err := fs.OpenFile(zipDoc) 42 if err != nil { 43 return err 44 } 45 defer fr.Close() 46 r, err := zip.NewReader(fr, zipDoc.ByteSize) 47 if err != nil { 48 return err 49 } 50 51 dirs := make(map[string]*vfs.DirDoc) 52 for _, f := range r.File { 53 f.Name = utils.CleanUTF8(f.Name) 54 name := path.Base(f.Name) 55 dirname := path.Dir(f.Name) 56 dir := dstDoc 57 if dirname != "." { 58 var ok bool 59 dirname = path.Join(dstDoc.Fullpath, dirname) 60 if dir, ok = dirs[dirname]; !ok { 61 dir, err = vfs.MkdirAll(fs, dirname) 62 if err != nil { 63 if couchdb.IsConflictError(err) { 64 dirname = fmt.Sprintf("%s - conflict - %d", dirname, time.Now().Unix()) 65 dir, err = vfs.MkdirAll(fs, dirname) 66 } 67 if err != nil { 68 return err 69 } 70 } 71 dirs[dirname] = dir 72 } 73 } 74 75 if f.Mode().IsDir() { 76 continue 77 } 78 79 rc, err := f.Open() 80 if err != nil { 81 return err 82 } 83 84 size := int64(f.UncompressedSize64) 85 mime, class := vfs.ExtractMimeAndClassFromFilename(f.Name) 86 mod := f.Modified 87 doc, err := vfs.NewFileDoc(name, dir.ID(), size, nil, mime, class, mod, false, false, false, nil) 88 if err != nil { 89 return err 90 } 91 doc.CozyMetadata = vfs.NewCozyMetadata("") 92 at := doc.CozyMetadata.CreatedAt 93 doc.CozyMetadata.UploadedAt = &at 94 file, err := fs.CreateFile(doc, nil) 95 if err != nil { 96 if couchdb.IsConflictError(err) { 97 doc.DocName = fmt.Sprintf("%s - conflict - %d", doc.DocName, time.Now().Unix()) 98 file, err = fs.CreateFile(doc, nil) 99 } 100 if err != nil { 101 return err 102 } 103 } 104 _, err = io.Copy(file, rc) 105 cerr := file.Close() 106 if err != nil { 107 return err 108 } 109 if cerr != nil { 110 return cerr 111 } 112 } 113 return nil 114 }