c語言,批次處理檔案,進行gzip壓縮

Please Call me 小强發表於2024-11-28
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/stat.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
#include <libgen.h>
#include <stdbool.h>
#include <assert.h>
#include <sys/types.h>
#include <fcntl.h>
#include <zlib.h>
#include <errno.h>

#define MAX_THREADS 16
#define MAX_QUEUE 100000
#define PATH_MAX 512
#define CHUNK 16384

char* ignoreUUIDs = NULL;
char* base_dir = NULL;
char* cache_dir = NULL;
char* astc_bin = NULL;

typedef struct {
    void (*function)(void *arg);
    void *arg;
} Task;

typedef struct {
    Task task_queue[MAX_QUEUE];
    int front;
    int rear;
    int count;
    int total_tasks;// 總共任務數
    int done_tasks;// 完成任務數
    pthread_mutex_t lock;
    pthread_cond_t cond_task;
} ThreadPool;

ThreadPool pool;
int gzip_compress(const char *source, const char *out ) {
    FILE *src = fopen(source, "rb");
    if (!src) {
        perror("開啟原始檔失敗");
        return -1;
    }

    // gzip 檔案處理
    gzFile dest_file = gzopen(out, "wb9"); // 使用最高壓縮等級 '9'
    if (!dest_file) {
        perror("開啟目的檔案失敗");
        fclose(src);
        return -1;
    }

    char inbuf[CHUNK];
    int num_read;
    while ((num_read = fread(inbuf, 1, sizeof(inbuf), src)) > 0) {
        // 寫入壓縮檔案
        if (gzwrite(dest_file, inbuf, num_read) != num_read) {
            perror("寫入壓縮檔案失敗");
            gzclose(dest_file);
            fclose(src);
            return -1;
        }
    }

    // 關閉檔案
    gzclose(dest_file);
    fclose(src);

    return 0;
}

// 新增任務到佇列
void add_task(ThreadPool *pool, void (*function)(void *), void *arg) {
    pthread_mutex_lock(&pool->lock);
    assert(pool->count < MAX_QUEUE);
    Task task;
    task.function = function;
    task.arg = arg;

    pool->task_queue[pool->rear] = task;
    pool->rear = (pool->rear + 1) % MAX_QUEUE;
    pool->count++;
    pthread_cond_signal(&pool->cond_task);
    pthread_mutex_unlock(&pool->lock);
}

// 執行緒要執行的任務
void *worker(void *arg) {
    while (1) {
        Task task;
        
        // 從佇列取出任務時需要確保正確的鎖搭配
        pthread_mutex_lock(&pool.lock);
        
        while (pool.count == 0) {
            if(pool.total_tasks && pool.done_tasks >= pool.total_tasks) {
                pthread_mutex_unlock(&pool.lock);
                goto END;
            }
            pthread_cond_wait(&pool.cond_task, &pool.lock);
        }

        task = pool.task_queue[pool.front];
        pool.front = (pool.front + 1) % MAX_QUEUE;
        pool.count--;
        pthread_mutex_unlock(&pool.lock);

        (*(task.function))(task.arg);

        pthread_mutex_lock(&pool.lock);
        pool.done_tasks++;
        if(pool.done_tasks >= pool.total_tasks) {
            pthread_cond_broadcast(&pool.cond_task);
            pthread_mutex_unlock(&pool.lock);
            break;
        }
        pthread_mutex_unlock(&pool.lock);
    }
END:
    printf("worker %d: exit : %d, %d\n",*(int*)arg, pool.done_tasks, pool.total_tasks);
    return NULL;
}

// 初始化執行緒池
void init_thread_pool(ThreadPool *pool, pthread_t threads[]) {
    pool->front = 0;
    pool->rear = 0;
    pool->count = 0;
    pool->total_tasks = 0;
    pool->done_tasks = 0;

    pthread_mutex_init(&pool->lock, NULL);
    pthread_cond_init(&pool->cond_task, NULL);

    for (int i = 0; i < MAX_THREADS; i++) {
        int *arg = malloc(sizeof(*arg));
        *arg = i;
        pthread_create(&threads[i], NULL, worker, arg);
    }
}

void task_function(void *arg) {
    char* image = (char *)arg;

    char imageGZ[PATH_MAX];
    strcpy(imageGZ, image);
    strcat(imageGZ, ".gz");
    assert(gzip_compress(image, imageGZ)==0);
    assert(rename(imageGZ, image)==0);
}

typedef struct {
    char **files;
    size_t count;
    size_t capacity;
} FileList;

void init_file_list(FileList *list) {
    list->count = 0;
    list->capacity = 100; // 初始容量
    list->files = malloc(list->capacity * sizeof(char *));
    if (list->files == NULL) {
        perror("Failed to allocate memory");
        exit(EXIT_FAILURE);
    }
}

void add_file(FileList *list, const char *file_path) {
    if (list->count >= list->capacity) {
        list->capacity *= 2;
        list->files = realloc(list->files, list->capacity * sizeof(char *));
        if (list->files == NULL) {
            perror("Failed to reallocate memory");
            exit(EXIT_FAILURE);
        }
    }
    list->files[list->count] = strdup(file_path);
    if (list->files[list->count] == NULL) {
        perror("Failed to duplicate string");
        exit(EXIT_FAILURE);
    }
    list->count++;
}

void free_file_list(FileList *list) {
    for (size_t i = 0; i < list->count; i++) {
        free(list->files[i]);
    }
    free(list->files);
}

int mkdir_p(const char *path) {
    char tmp[512];
    char *p = NULL;
    size_t len;

    snprintf(tmp, sizeof(tmp), "%s", path);
    len = strlen(tmp);
    if (tmp[len - 1] == '/')
        tmp[len - 1] = 0;

    for (p = tmp + 1; *p; p++) {
        if (*p == '/') {
            *p = 0;
            if (mkdir(tmp, S_IRWXU) != 0) {
                if (errno != EEXIST) {
                    return -1;
                }
            }
            *p = '/';
        }
    }
    if (mkdir(tmp, S_IRWXU) != 0) {
        if (errno != EEXIST) {
            return -1;
        }
    }
    return 0;
}

int check_file_extension(const char *filename) {
    const char *dot = strrchr(filename, '.');
    if (!dot || dot == filename) {
        return 0;
    }

    const char* extension = dot + 1;

    if (strcmp(extension, "html") == 0) {
        return 2;
    }
    return 0;
}

void find_images(const char *directory, FileList *file_list) {
    DIR *dir;
    struct dirent *entry;
    struct stat entry_info;

    if ((dir = opendir(directory)) == NULL) {
        perror("Unable to open directory");
        return;
    }

    while ((entry = readdir(dir)) != NULL) {
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
            continue; // 跳過當前目錄和父目錄
        }

        char path[1024];
        snprintf(path, sizeof(path), "%s/%s", directory, entry->d_name);

        if (stat(path, &entry_info) == 0) {
            if (S_ISDIR(entry_info.st_mode)) {
                // 如果是目錄,遞迴呼叫
                find_images(path, file_list);
            } else if (S_ISREG(entry_info.st_mode)) {
                // 如果是常規檔案,檢查副檔名
                if (!check_file_extension(entry->d_name)) {
                    add_file(file_list, path);
                }
            }
        }
    }

    closedir(dir);
}

int main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <source_dir> \n", argv[0]);
        return EXIT_FAILURE;
    }
    const char *source_dir = argv[1];

    FileList image_files;
    init_file_list(&image_files);

    pthread_t threads[MAX_THREADS];
    init_thread_pool(&pool, threads);
    find_images(source_dir, &image_files);
    pool.total_tasks = image_files.count;

    if(image_files.count == 0){
        goto END;
    }
    // 新增一些任務到佇列中
    for (size_t i = 0; i < image_files.count; i++) {
        // printf("%s\n", image_files.files[i]);
        add_task(&pool, task_function, image_files.files[i]);
    }

    for (int i = 0; i < MAX_THREADS; i++) {
        pthread_join(threads[i], NULL);
    }

    free_file_list(&image_files);

    // 等待執行緒池自己退出
    pthread_mutex_lock(&pool.lock);
    pthread_cond_broadcast(&pool.cond_task);
    pthread_mutex_unlock(&pool.lock);
END:
    printf("All tasks completed. Exiting main.\n");
    return 0;
}

相關文章