rykit-v3
Version:
A powerful Node.js utility library for Minecraft server status, color manipulation, encryption, file operations, and more
363 lines (315 loc) • 9.83 kB
JavaScript
import { promises as fs } from "fs";
import path from "path";
import { Buffer } from "buffer";
import { lookup } from "mime-types";
import { createReadStream, createWriteStream } from "fs";
import { pipeline } from "stream/promises";
import zlib from "zlib";
class FileUtils {
constructor() {
this.encoding = "utf-8";
this.maxFileSize = 50 * 1024 * 1024; // 50MB varsayılan limit
}
// File reading operations
async readFile(filePath) {
try {
return await fs.readFile(filePath, this.encoding);
} catch (error) {
throw new Error(`Failed to read file: ${error.message}`);
}
}
async readJSON(filePath) {
try {
const content = await this.readFile(filePath);
return JSON.parse(content);
} catch (error) {
throw new Error(`Failed to parse JSON file: ${error.message}`);
}
}
// File writing operations
async writeFile(filePath, content) {
try {
await fs.writeFile(filePath, content, this.encoding);
return true;
} catch (error) {
throw new Error(`Failed to write file: ${error.message}`);
}
}
async writeJSON(filePath, data, pretty = true) {
try {
const content = pretty
? JSON.stringify(data, null, 2)
: JSON.stringify(data);
return await this.writeFile(filePath, content);
} catch (error) {
throw new Error(`Failed to write JSON file: ${error.message}`);
}
}
// File operations
async copyFile(source, destination) {
try {
await fs.copyFile(source, destination);
return true;
} catch (error) {
throw new Error(`Failed to copy file: ${error.message}`);
}
}
async moveFile(source, destination) {
try {
await fs.rename(source, destination);
return true;
} catch (error) {
throw new Error(`Failed to move file: ${error.message}`);
}
}
async deleteFile(filePath) {
try {
await fs.unlink(filePath);
return true;
} catch (error) {
throw new Error(`Failed to delete file: ${error.message}`);
}
}
// Directory operations
async createDirectory(dirPath) {
try {
await fs.mkdir(dirPath, { recursive: true });
return true;
} catch (error) {
throw new Error(`Failed to create directory: ${error.message}`);
}
}
async readDirectory(dirPath) {
try {
return await fs.readdir(dirPath);
} catch (error) {
throw new Error(`Failed to read directory: ${error.message}`);
}
}
async deleteDirectory(dirPath) {
try {
await fs.rm(dirPath, { recursive: true, force: true });
return true;
} catch (error) {
throw new Error(`Failed to delete directory: ${error.message}`);
}
}
// Path operations
async exists(path) {
try {
await fs.access(path);
return true;
} catch {
return false;
}
}
async isFile(filePath) {
try {
const stats = await fs.stat(filePath);
return stats.isFile();
} catch {
return false;
}
}
async isDirectory(dirPath) {
try {
const stats = await fs.stat(dirPath);
return stats.isDirectory();
} catch {
return false;
}
}
// File info
async getFileInfo(filePath) {
try {
const stats = await fs.stat(filePath);
return {
size: stats.size,
created: stats.birthtime,
modified: stats.mtime,
accessed: stats.atime,
isFile: stats.isFile(),
isDirectory: stats.isDirectory(),
};
} catch (error) {
throw new Error(`Failed to get file info: ${error.message}`);
}
}
// Path utilities
getExtension(filePath) {
return path.extname(filePath);
}
getFileName(filePath) {
return path.basename(filePath);
}
getDirectory(filePath) {
return path.dirname(filePath);
}
joinPaths(...paths) {
return path.join(...paths);
}
// Base64 operations
async fileToBase64(filePath) {
try {
const buffer = await fs.readFile(filePath);
return buffer.toString("base64");
} catch (error) {
throw new Error(`Failed to convert file to base64: ${error.message}`);
}
}
async base64ToFile(base64String, filePath) {
try {
const buffer = Buffer.from(base64String, "base64");
await fs.writeFile(filePath, buffer);
return true;
} catch (error) {
throw new Error(`Failed to save base64 to file: ${error.message}`);
}
}
// File type detection
getMimeType(filePath) {
return lookup(filePath) || "application/octet-stream";
}
async detectFileType(filePath) {
try {
const buffer = await fs.readFile(filePath, { length: 4100 });
return {
mimeType: this.getMimeType(filePath),
extension: path.extname(filePath),
size: buffer.length,
encoding: this.detectEncoding(buffer),
};
} catch (error) {
throw new Error(`Failed to detect file type: ${error.message}`);
}
}
detectEncoding(buffer) {
// Basic encoding detection
const utf8Pattern = /^[\x09\x0A\x0D\x20-\x7E]*$/;
const sample = buffer.toString().slice(0, 1000);
return utf8Pattern.test(sample) ? "utf8" : "binary";
}
// File size optimization
async compressFile(sourcePath, destinationPath) {
try {
const gzip = zlib.createGzip();
const source = createReadStream(sourcePath);
const destination = createWriteStream(destinationPath);
await pipeline(source, gzip, destination);
return true;
} catch (error) {
throw new Error(`Failed to compress file: ${error.message}`);
}
}
async decompressFile(sourcePath, destinationPath) {
try {
const gunzip = zlib.createGunzip();
const source = createReadStream(sourcePath);
const destination = createWriteStream(destinationPath);
await pipeline(source, gunzip, destination);
return true;
} catch (error) {
throw new Error(`Failed to decompress file: ${error.message}`);
}
}
// File upload/download helpers
async uploadFile(sourcePath, destinationPath, options = {}) {
try {
const stats = await fs.stat(sourcePath);
// Check file size
if (stats.size > (options.maxSize || this.maxFileSize)) {
throw new Error("File size exceeds limit");
}
// Check file type if specified
if (options.allowedTypes) {
const mimeType = this.getMimeType(sourcePath);
if (!options.allowedTypes.includes(mimeType)) {
throw new Error("File type not allowed");
}
}
// Create destination directory if it doesn't exist
await fs.mkdir(path.dirname(destinationPath), { recursive: true });
// Copy file with progress tracking
const source = createReadStream(sourcePath);
const destination = createWriteStream(destinationPath);
let bytesTransferred = 0;
source.on("data", (chunk) => {
bytesTransferred += chunk.length;
if (options.onProgress) {
const progress = (bytesTransferred / stats.size) * 100;
options.onProgress(progress);
}
});
await pipeline(source, destination);
return {
size: stats.size,
mimeType: this.getMimeType(sourcePath),
path: destinationPath,
};
} catch (error) {
throw new Error(`Failed to upload file: ${error.message}`);
}
}
async downloadFile(url, destinationPath, options = {}) {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const contentLength = response.headers.get("content-length");
const contentType = response.headers.get("content-type");
// Check file size if content-length is available
if (
contentLength &&
parseInt(contentLength) > (options.maxSize || this.maxFileSize)
) {
throw new Error("File size exceeds limit");
}
// Check content type if specified - daha esnek kontrol
if (options.allowedTypes) {
const isAllowed = options.allowedTypes.some(
(type) =>
contentType?.includes(type) ||
type === "*/*" ||
type === contentType
);
if (!isAllowed) {
throw new Error(
`File type ${contentType} not allowed. Allowed types: ${options.allowedTypes.join(
", "
)}`
);
}
}
// Create destination directory if it doesn't exist
await fs.mkdir(path.dirname(destinationPath), { recursive: true });
const destination = createWriteStream(destinationPath);
const body = response.body;
if (body.pipe) {
await pipeline(body, destination);
} else {
const buffer = await response.arrayBuffer();
await fs.writeFile(destinationPath, Buffer.from(buffer));
}
return {
size: contentLength,
mimeType: contentType,
path: destinationPath,
};
} catch (error) {
throw new Error(`Failed to download file: ${error.message}`);
}
}
// File size formatting helper
formatFileSize(bytes) {
const units = ["B", "KB", "MB", "GB", "TB"];
let size = bytes;
let unitIndex = 0;
while (size >= 1024 && unitIndex < units.length - 1) {
size /= 1024;
unitIndex++;
}
return `${size.toFixed(2)} ${units[unitIndex]}`;
}
}
export default FileUtils;