github.com/cozy/cozy-stack@v0.0.0-20240603063001-31110fa4cae1/worker/archive/zip.go (about)

     1  package archive
     2  
     3  import (
     4  	"archive/zip"
     5  	"io"
     6  	"time"
     7  
     8  	"github.com/cozy/cozy-stack/model/job"
     9  	"github.com/cozy/cozy-stack/model/vfs"
    10  )
    11  
    12  type zipMessage struct {
    13  	Files    map[string]string `json:"files"` // path -> fileID
    14  	DirID    string            `json:"dir_id"`
    15  	Filename string            `json:"filename"`
    16  }
    17  
    18  // WorkerZip is a worker that creates zip archives.
    19  func WorkerZip(ctx *job.TaskContext) error {
    20  	msg := &zipMessage{}
    21  	if err := ctx.UnmarshalMessage(msg); err != nil {
    22  		return err
    23  	}
    24  	fs := ctx.Instance.VFS()
    25  	return createZip(fs, msg.Files, msg.DirID, msg.Filename)
    26  }
    27  
    28  func createZip(fs vfs.VFS, files map[string]string, dirID, filename string) error {
    29  	now := time.Now()
    30  	zipDoc, err := vfs.NewFileDoc(filename, dirID, -1, nil, "application/zip", "zip", now, false, false, false, nil)
    31  	if err != nil {
    32  		return err
    33  	}
    34  	zipDoc.CozyMetadata = vfs.NewCozyMetadata("")
    35  	zipDoc.CozyMetadata.UploadedAt = &now
    36  	z, err := fs.CreateFile(zipDoc, nil)
    37  	if err != nil {
    38  		return err
    39  	}
    40  	w := zip.NewWriter(z)
    41  	for filePath, fileID := range files {
    42  		err = addFileToZip(fs, w, fileID, filePath)
    43  		if err != nil {
    44  			break
    45  		}
    46  	}
    47  	werr := w.Close()
    48  	zerr := z.Close()
    49  	if err != nil {
    50  		return err
    51  	}
    52  	if werr != nil {
    53  		return werr
    54  	}
    55  	return zerr
    56  }
    57  
    58  func addFileToZip(fs vfs.VFS, w *zip.Writer, fileID, filePath string) error {
    59  	file, err := fs.FileByID(fileID)
    60  	if err != nil {
    61  		return err
    62  	}
    63  	fr, err := fs.OpenFile(file)
    64  	if err != nil {
    65  		return err
    66  	}
    67  	defer fr.Close()
    68  	header := &zip.FileHeader{
    69  		Name:     filePath,
    70  		Method:   zip.Deflate,
    71  		Modified: file.UpdatedAt,
    72  	}
    73  	if file.Executable {
    74  		header.SetMode(0750)
    75  	} else {
    76  		header.SetMode(0640)
    77  	}
    78  	f, err := w.CreateHeader(header)
    79  	if err != nil {
    80  		return err
    81  	}
    82  	_, err = io.Copy(f, fr)
    83  	return err
    84  }