-
-
Notifications
You must be signed in to change notification settings - Fork 390
Emitting progress #128
Comments
And could there be a way to abort archive/unarchive programmatically? |
Here's an example for unarchiving using https://github.com/cheggaaa/pb func createCountFilesWalker(accumulator *int) func(archiver.File) error {
return func(f archiver.File) error {
*accumulator++
return nil
}
}
func createExtractFilesWalker(bar *pb.ProgressBar, archiveHasBaseDir bool, outputDir string) func(archiver.File) error {
pathSeparator := fmt.Sprintf("%c", os.PathSeparator)
return func(f archiver.File) error {
if app.ProgressMeters() {
bar.Increment()
}
name := f.Name()
switch h := f.Header.(type) {
case zip.FileHeader:
name = h.Name
case *tar.Header:
name = h.Name
case *rardecode.FileHeader:
name = h.Name
default:
return fmt.Errorf("Unable to process %v", h)
}
dirname := filepath.ToSlash(filepath.Join(outputDir, filepath.Dir(name)))
if archiveHasBaseDir {
namePathParts := strings.Split(filepath.Dir(name), pathSeparator)
dirname = filepath.ToSlash(filepath.Join(outputDir, filepath.Join(namePathParts[1:]...)))
}
if f.IsDir() {
log.Traceln("Making directory", dirname)
os.MkdirAll(dirname, 0755)
return nil
}
outFile := filepath.ToSlash(filepath.Join(dirname, filepath.Base(name)))
log.Traceln("Writing file", outFile)
data := make([]byte, f.Size())
numBytesRead, err := f.Read(data)
if err != nil && !(numBytesRead == int(f.Size()) && err == io.EOF) {
return err
}
err = ioutil.WriteFile(outFile, data, 0644)
return err
}
}
// Unarchive a given inputFile to an outputDir
func Unarchive(inputFile string, outputDir string, archiveHasBaseDir bool) {
inputFile = filepath.ToSlash(inputFile)
outputDir = filepath.ToSlash(outputDir)
if isDir, _ := IsDir(outputDir); isDir {
err := os.RemoveAll(outputDir)
if err != nil {
log.Panic(err)
}
}
log.Debugln("Extracting", inputFile, "to", outputDir)
var archiveEntries = 0
countFilesWalker := createCountFilesWalker(&archiveEntries)
archiver.Walk(inputFile, countFilesWalker)
log.Debugln(archiveEntries, " files/folders counted via walking")
var bar *pb.ProgressBar
if app.ProgressMeters() {
// Create and start bar
bar = pb.New(archiveEntries).SetRefreshRate(time.Millisecond * 20).Prefix(fmt.Sprintf("Extracting %s ", filepath.Base(inputFile)))
bar.ShowTimeLeft = false
// bar.ShowSpeed = true
bar.ShowFinalTime = false
bar.Start()
}
extractFilesWalker := createExtractFilesWalker(bar, archiveHasBaseDir, outputDir)
err := archiver.Walk(inputFile, extractFilesWalker)
if err != nil {
log.Panic(err)
}
if app.ProgressMeters() {
bar.Finish()
}
log.Debugln("Successfully extracted", inputFile, "to", outputDir)
} |
A ProgressCallback function would be better here (implemented in library). Something like https://godoc.org/github.com/itchio/sevenzip-go/sz#ExtractCallbackFuncs |
During the holidays I took some time and completely rewrote this library in #302 and am preparing to merge and tag v4. I gave this issue a lot of thought, and realized there's lots of ways to count progress and I'm not sure there's any one good answer. Fortunately, in v4, all the core APIs are stream-oriented/ so you can emit progress yourself. For example, to count bytes, I whipped up this gem: type ProgressReader struct {
io.Reader
n int64
N chan<- int64
}
func (pr *ProgressReader) Read(p []byte) (n int, err error) {
n, err = pr.Reader.Read(p)
if n > 0 {
pr.n += int64(n)
select {
case pr.N <- pr.n:
default:
}
}
return
}
type ProgressWriter struct {
io.Writer
n int64
N chan<- int64
}
func (pw *ProgressWriter) Write(p []byte) (n int, err error) {
n, err = pw.Writer.Write(p)
if n > 0 {
pw.n += int64(n)
select {
case pw.N <- pw.n:
default:
}
}
return
} Or you could count progress by files by using the |
What would you like to have changed?
Emit percentage on archiving/unarchiving files to be able to count progress
Why is this feature a useful, necessary, and/or important addition to this project?
It allows to show the user a status of his files being archived/unarchived.
The text was updated successfully, but these errors were encountered: