Home >Backend Development >Golang >Organize your downloads in GO

Organize your downloads in GO

Linda Hamilton
Linda HamiltonOriginal
2024-10-31 05:44:301039browse

Organizador de seus downloads em GO

Hello, ions here again.

The fear of learning something that an AI will one day accomplish leaves me completely distressed. But, if "solving problems" is still a requirement imposed on human beings of the future, why not persist?

This time I bring another tutorial. Less useless than the first. So let's define the structures of "problems", because we already know one thing: those who don't have problems, it's because they didn't look enough. And for those who haven't found them yet, it's just a matter of time before they can create them.

Project structure

The simplest structure of the program is:

  • Scan a folder (for example the downloads folder or another directory)
  • Identify the type of each file in the directory in question
  • Move the file to a subfolder corresponding to its type (images, videos, documents...)

Starting the project

Create a directory and navigate to it:

mkdir organizador
cd organizador

Create an organizer.go file and start its modules:

touch organizador.go
go mod init organizador.go

You should have something more or less like this:

~/organizador
.
├── go.mod
└── organizador.go

Part 1: Checking if a directory exists

Let's define the source directorydirOrigem in which we will perform the organization. With it defined, let's check if it actually exists, otherwise we will return an error:

package main

import (
    "fmt"
    "os"
)

// Defina o deretório o qual você quer organizar como variável global
var dirOrigem string := "/Users/User/Downloads" // Troque o diretório 

func main() {

    // Verificar se o diretório existe, caso contrário, retornar erro
    if _, err := os.Stat(dirOrigem); os.IsNotExist(err) {
        fmt.Println("BAD DIR :( \nDiretório não encontrado: ", dirOrigem)
        return
    } else {
        // Imprimir mensagem caso o diretório exista
        fmt.Println("GOOD DIR :) \nDiretório encontrado: ", dirOrigem)
    }
}

Now, let's make some considerations about the code above:

  • The os.Stat function returns two values ​​of type os.FileInfo and error err.
  1. FileInfo is an interface that returns detailed file information, which in this case, we don't need. Therefore, to ignore this interface, we use _,. However, we don't want to ignore the error: if _, err := ...
  2. We pass the error err as a parameter to the os.IsNotExist() function, because if the directory does not exist, the os.Stat() function will return a NOT NULL error, causing the os. .IsNotExist() returns true, executing our message: BAD DIR :(
  3. If os.IsNotExist() returns false, we will print the message from the else condition: GOOD DIR :)

Part 2: Callback function concept is crazy, man!

Did you notice that here we are going little by little and enjoying the bits and bytes to the sound of the mechanical keyboard. _Tchaka tchaka boom! _

And now we are going to create a callback function. Something that I had never actually learned about, or was never curious enough to question if I ever used this concept in any Python code in my pre-golang life.

A callback function is a function passed with an argument to another function.

If you are already familiar with the concept, congrats, otherwise, congrats. In other words, congrats!

Now let's create a filepath.Walk callback function that will be passed as an argument to another function.

mkdir organizador
cd organizador
touch organizador.go
go mod init organizador.go

But wait, how does filepath.Walk call the Callback Function?

When you call filepath.Walk(sourcedir, listFiles), the filepath.Walk function does the heavy lifting of walking through all the files and subdirectories within sourcedir.

For each file or directory found, it calls the listFiles function with three arguments:

  • path: The full path to the current file or directory.
  • info: an os.FileInfo object that contains information about the file/directory (such as name, whether it is a directory, size, etc.).
  • err: an error, if something goes wrong when accessing this file or directory.

Go automatically understands that listFiles must receive these three parameters because filepath.Walk expects a function that follows exactly this signature:

~/organizador
.
├── go.mod
└── organizador.go

Notice that the Walk function returns an error! This is relevant!

That's why we equate our function filepath.Walk(dirOrigem,listarArquivos) to an err:

package main

import (
    "fmt"
    "os"
)

// Defina o deretório o qual você quer organizar como variável global
var dirOrigem string := "/Users/User/Downloads" // Troque o diretório 

func main() {

    // Verificar se o diretório existe, caso contrário, retornar erro
    if _, err := os.Stat(dirOrigem); os.IsNotExist(err) {
        fmt.Println("BAD DIR :( \nDiretório não encontrado: ", dirOrigem)
        return
    } else {
        // Imprimir mensagem caso o diretório exista
        fmt.Println("GOOD DIR :) \nDiretório encontrado: ", dirOrigem)
    }
}

After all, because it returns an error, it is an error XD

Example in Action

Here's a more detailed look at what happens at each step:

func main() { 
// Restante do código
.
.
.

// Percorrer e listar os arquivos no diretório dirOrigem
    err := filepath.Walk(dirOrigem, listarArquivos)
    if err != nil {
        fmt.Println("Erro ao percorrer o diretório: ", err)
    }
}

For each file or directory in dirOrigem, filepath.Walk will call listFiles as if it were something like this:

// Função que lista os arquivos do diretório
func listarArquivos(caminho string, info os.FileInfo, err error) error {
    if err != nil {
        return err
    }

    // Ignorar diretórios e exibir apenas arquivos
    if !info.IsDir() && !strings.HasPrefix(info.Name(), ".") {
        fmt.Println("Arquivo encontrado: ", info.Name())
    }
    return nil
}

In this example, for each call:

  • path: receives the path of the file or directory.
  • info: contains information about this item (such as name and type).
  • err: is used to catch any specific error while accessing the file/directory.

Callback function

listFiles is a callback function that filepath.Walk automatically calls with these values. This way, we don't need to worry about setting the path, info and err values; filepath.Walk already does this for us.

PHE!

Now do that naughty test on your terminal:

// Função Walk()
func Walk(root string, walkFn WalkFunc) error

// Tipo WalkFunc
type WalkFunc func(path string, info os.FileInfo, err error) error

You can have the result:

err := filepath.Walk(dirOrgiem, listarArquivos)

Or:

//Percorrer e listar os arquivos no diretório
err := filepath.Walk(dirOrigem, listarArquivos)

In this case I just put an extra "s" in "Downloads" so that the Origin dir would be incorrect.

Now delete the listFiles function, as we are not going to use it.

Just kidding, we're just going to change her name and add other logic.

PART 3: ORGANIZE != ORGANIZED

Organized is good, organizing is awesome.

After this brilliant observation on my part, let's move on to the part that really interests us: organizing the whole thing.

As an irony in life, before organizing the files, we have to organize our ideas for the next steps.

Our next function basically needs:

  • Create subfolders based on the extensions of each file in our dirOrigem directory, if it does not exist.
  • Move the files to their respective folders according to their extensions.
  • But if the files are already in the organizing subfolders, it should not create them again.

Let's understand what each part of this code does:

mkdir organizador
cd organizador

Structure of the organizeFiles function

The organizeFiles function is called for each file or folder found in the directory structure. It checks the conditions to organize each file based on its extension, creating subfolders and moving files if necessary.

touch organizador.go
go mod init organizador.go

Here, the organizeFiles function takes three parameters:

  • path: the full path to the current file or directory.
  • info: file or directory information, obtained from type os.FileInfo.
  • err: a possible error that may occur when trying to access the item.

The first check is whether there is an error when accessing the file/directory. If so, it is returned immediately.

Filtering files and ignoring hidden directories

~/organizador
.
├── go.mod
└── organizador.go

This snippet makes two checks:

  • !info.IsDir(): checks if the item is not a directory (that is, it is a file).
  • !strings.HasPrefix(info.Name(), "."): checks that the file name does not start with ".", ignoring hidden files on Unix-based systems.

If both conditions are met, the file is displayed with fmt.Println.

Identifying the file extension and creating the subfolder name

package main

import (
    "fmt"
    "os"
)

// Defina o deretório o qual você quer organizar como variável global
var dirOrigem string := "/Users/User/Downloads" // Troque o diretório 

func main() {

    // Verificar se o diretório existe, caso contrário, retornar erro
    if _, err := os.Stat(dirOrigem); os.IsNotExist(err) {
        fmt.Println("BAD DIR :( \nDiretório não encontrado: ", dirOrigem)
        return
    } else {
        // Imprimir mensagem caso o diretório exista
        fmt.Println("GOOD DIR :) \nDiretório encontrado: ", dirOrigem)
    }
}

Here:

  • strings.ToLower(filepath.Ext(info.Name())): extracts the file extension (e.g. .txt) and transforms it to lowercase to ensure consistency.
  • subfolder := filepath.Join(sourcedir, extension[1:]): creates the full path of the subfolder where the file will be moved. The extension[1:] removes the starting point (.) from the extension, forming the name of the subfolder, such as txt.

Creating the subfolder if it doesn't already exist

func main() { 
// Restante do código
.
.
.

// Percorrer e listar os arquivos no diretório dirOrigem
    err := filepath.Walk(dirOrigem, listarArquivos)
    if err != nil {
        fmt.Println("Erro ao percorrer o diretório: ", err)
    }
}

Here, the function:

  • Check if the subfolder already exists using os.Stat.
  • If the subfolder does not exist (os.IsNotExist(err)), it is created with os.Mkdir(subfolder, os.ModePerm).
  • os.ModePerm sets the default permissions for the new folder. If there is an error when creating the folder, it is displayed and returned.

Setting the file destination path

mkdir organizador
cd organizador

At this point, destinationPath represents the final path where the file will be moved. It is constructed using filepath.Join, to join the subfolder path to the filename.

Checking if the file is already in the destination folder

touch organizador.go
go mod init organizador.go
  • This snippet compares the destination path with the current file path. If they are the same, it means the file is already in the correct subfolder, so it is ignored with a message (fmt.Printf).
  • Otherwise, os.Rename(path, destinationPath) moves the file to the subfolder. If there is an error during the move, it is returned.

Final Summary

The function:

  1. Scroll through a directory, checking each item.
  2. Ignores hidden directories and files.
  3. Determines the file extension and thus the destination subfolder.
  4. Create the subfolder (if it doesn't already exist).
  5. Moves the file to the subfolder unless it is already there.

Using filepath.Walk(dirOrigem, organizeFiles) passes this function to each file within the directory, causing them all to be organized automatically.

This code fits well as a file organization function because it handles the creation and movement logic in a single function – an efficient and organized form of structure.

REPO: https://github.com/ionnss/organizador


***Another day on earth,
ions

The above is the detailed content of Organize your downloads in GO. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn