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  }