文章目录
-
- 在当今数字化时代,网站的功能性需求日益复杂。无论是企业官网、个人博客,还是电子商务平台,文件上传与管理功能已成为不可或缺的基础需求。用户期望能够轻松上传图片、文档、视频等内容,而网站管理员则需要高效、安全地管理这些文件资源。 传统的WordPress媒体库虽然提供了基础的文件上传功能,但在面对大量文件、大文件上传、多用户协作、跨平台访问等复杂场景时,往往显得力不从心。云存储解决方案的出现,为这一问题提供了完美的答案。通过将文件存储在云端,网站不仅可以减轻服务器负担,还能实现更高的可用性、可扩展性和安全性。 本文将深入探讨如何通过WordPress代码二次开发,为网站添加强大的文件上传与云存储管理功能,并在此基础上实现一系列常用互联网小工具,从而大幅提升网站的功能性和用户体验。
-
- WordPress内置了一个相对完整的文件上传系统。当用户通过媒体上传界面或相关功能上传文件时,系统会执行以下关键步骤: 文件验证:检查文件类型、大小是否符合系统设置 安全处理:对文件名进行清理,防止安全漏洞 存储处理:将文件保存到wp-content/uploads目录,并按年月组织子目录 数据库记录:在wp_posts表中创建attachment类型的记录 元数据生成:为图片文件生成缩略图,提取EXIF信息等 了解这一基础流程是进行二次开发的前提。我们可以通过分析wp_handle_upload()、media_handle_upload()等核心函数,掌握WordPress处理上传文件的完整机制。
- 尽管WordPress默认系统能够满足基本需求,但在实际应用中存在明显不足: 存储空间受限:依赖服务器本地存储,空间有限且不易扩展 性能瓶颈:大文件上传和处理可能拖慢网站响应速度 备份困难:文件分散在服务器上,难以实现统一备份和恢复 访问限制:缺乏细粒度的访问控制和权限管理 多站点管理复杂:对于多站点网络,文件管理变得异常复杂 这些局限性正是我们需要引入云存储解决方案的根本原因。
-
- 目前市场上有多种云存储服务可供选择,每种都有其特点和适用场景: Amazon S3:功能全面,生态系统完善,适合企业级应用 Google Cloud Storage:与Google生态系统深度集成,性能优异 阿里云OSS:国内访问速度快,符合中国法规要求 腾讯云COS:性价比高,与腾讯生态整合良好 Backblaze B2:价格实惠,适合个人和小型企业 Wasabi:无出口费用,适合高频访问场景
- 在选择云存储服务时,需要综合考虑以下因素: 成本结构:存储费用、请求费用、流量费用的综合评估 性能表现:上传下载速度、延迟、可用性保证 地理位置:服务器位置对访问速度的影响 集成难度:API的易用性和文档完整性 合规要求:数据主权、隐私保护等法规遵从性 生态系统:与其他服务的集成能力
- 在开始自定义开发前,了解现有插件解决方案是必要的: WP Offload Media:功能全面,支持多种云服务 Media Cloud:专注于云存储,提供高级功能 Stateless:与Google Cloud Platform深度集成 Storage for WordPress:轻量级解决方案,易于定制 虽然这些插件提供了现成的解决方案,但通过自定义开发,我们可以实现更贴合特定需求、更高效集成的文件管理系统。
-
- 我们的自定义系统将采用模块化设计,主要包括以下组件: 上传处理模块:负责接收、验证和处理上传请求 云存储适配器:抽象不同云服务的API,提供统一接口 文件管理模块:提供文件的增删改查操作 权限控制系统:管理用户对文件的访问权限 缓存与优化层:提高系统性能和响应速度 管理界面:为管理员和用户提供友好的操作界面
-
- <?php /** * 云存储适配器抽象类 */ abstract class Cloud_Storage_Adapter { protected $config; protected $client; public function __construct($config) { $this->config = $config; $this->initialize_client(); } abstract protected function initialize_client(); abstract public function upload($local_path, $remote_path, $options = []); abstract public function download($remote_path, $local_path); abstract public function delete($remote_path); abstract public function list_files($prefix = '', $options = []); abstract public function get_url($remote_path, $expires = null); abstract public function file_exists($remote_path); }
- <?php /** * Amazon S3适配器实现 */ class S3_Storage_Adapter extends Cloud_Storage_Adapter { protected function initialize_client() { $this->client = new AwsS3S3Client([ 'version' => 'latest', 'region' => $this->config['region'], 'credentials' => [ 'key' => $this->config['key'], 'secret' => $this->config['secret'], ], ]); } public function upload($local_path, $remote_path, $options = []) { $default_options = [ 'Bucket' => $this->config['bucket'], 'Key' => $remote_path, 'SourceFile' => $local_path, 'ACL' => 'private', ]; $options = array_merge($default_options, $options); try { $result = $this->client->putObject($options); return [ 'success' => true, 'url' => $result['ObjectURL'], 'etag' => $result['ETag'], ]; } catch (AwsS3ExceptionS3Exception $e) { return [ 'success' => false, 'error' => $e->getMessage(), ]; } } // 其他方法实现... }
- <?php /** * 文件上传处理器 */ class File_Upload_Handler { private $adapter; private $allowed_types; private $max_size; public function __construct($adapter) { $this->adapter = $adapter; $this->allowed_types = get_option('allowed_upload_types', []); $this->max_size = get_option('max_upload_size', 10485760); // 默认10MB } public function handle_upload($file, $user_id, $options = []) { // 验证文件 $validation = $this->validate_file($file); if (!$validation['valid']) { return $validation; } // 生成唯一文件名和路径 $file_info = $this->generate_file_info($file, $user_id); // 临时保存文件 $temp_path = $this->save_temp_file($file); // 上传到云存储 $upload_result = $this->adapter->upload( $temp_path, $file_info['remote_path'], $options ); // 清理临时文件 unlink($temp_path); if ($upload_result['success']) { // 保存文件记录到数据库 $file_id = $this->save_file_record($file_info, $user_id); return [ 'success' => true, 'file_id' => $file_id, 'url' => $upload_result['url'], 'file_info' => $file_info, ]; } return [ 'success' => false, 'error' => $upload_result['error'], ]; } private function validate_file($file) { // 检查文件大小 if ($file['size'] > $this->max_size) { return [ 'valid' => false, 'error' => '文件大小超过限制', ]; } // 检查文件类型 $file_type = wp_check_filetype($file['name']); if (!in_array($file_type['type'], $this->allowed_types)) { return [ 'valid' => false, 'error' => '不支持的文件类型', ]; } // 安全检查 if (!wp_verify_nonce($_POST['upload_nonce'], 'file_upload')) { return [ 'valid' => false, 'error' => '安全验证失败', ]; } return ['valid' => true]; } private function generate_file_info($file, $user_id) { $original_name = sanitize_file_name($file['name']); $extension = pathinfo($original_name, PATHINFO_EXTENSION); $unique_name = wp_generate_uuid4() . '.' . $extension; // 按用户和日期组织目录结构 $date = date('Y/m'); $remote_path = "uploads/{$user_id}/{$date}/{$unique_name}"; return [ 'original_name' => $original_name, 'unique_name' => $unique_name, 'remote_path' => $remote_path, 'extension' => $extension, 'size' => $file['size'], ]; } // 其他辅助方法... }
- 为了高效管理文件元数据,我们需要创建自定义数据库表: CREATE TABLE wp_cloud_files ( id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, user_id BIGINT(20) UNSIGNED NOT NULL, original_name VARCHAR(255) NOT NULL, unique_name VARCHAR(255) NOT NULL, remote_path VARCHAR(500) NOT NULL, file_type VARCHAR(100) NOT NULL, file_size BIGINT(20) UNSIGNED NOT NULL, mime_type VARCHAR(100), upload_time DATETIME DEFAULT CURRENT_TIMESTAMP, last_access DATETIME, access_count INT UNSIGNED DEFAULT 0, is_public TINYINT(1) DEFAULT 0, metadata TEXT, PRIMARY KEY (id), INDEX user_index (user_id), INDEX path_index (remote_path(255)), INDEX type_index (file_type), INDEX upload_time_index (upload_time) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
- 创建用户友好的管理界面是系统成功的关键。我们可以使用WordPress的Admin API创建自定义管理页面: <?php /** * 文件管理界面 */ class File_Management_UI { public function __construct() { add_action('admin_menu', [$this, 'add_admin_menu']); add_action('admin_enqueue_scripts', [$this, 'enqueue_scripts']); } public function add_admin_menu() { add_menu_page( '云文件管理', '云文件', 'upload_files', 'cloud-file-manager', [$this, 'render_main_page'], 'dashicons-cloud', 30 ); add_submenu_page( 'cloud-file-manager', '上传文件', '上传', 'upload_files', 'cloud-file-upload', [$this, 'render_upload_page'] ); add_submenu_page( 'cloud-file-manager', '文件统计', '统计', 'manage_options', 'cloud-file-stats', [$this, 'render_stats_page'] ); } public function render_main_page() { ?> <div class="wrap"> <h1 class="wp-heading-inline">云文件管理</h1> <a href="<?php echo admin_url('admin.php?page=cloud-file-upload'); ?>" class="page-title-action">上传文件</a> <hr class="wp-header-end"> <div id="cloud-file-manager"> <!-- 文件列表将通过Vue.js动态加载 --> <div class="file-manager-container"> <div class="file-toolbar"> <div class="search-box"> <input type="search" id="file-search" placeholder="搜索文件..."> </div> <div class="filter-options"> <select id="file-type-filter"> <option value="">所有类型</option> <option value="image">图片</option> <option value="document">文档</option> <option value="video">视频</option> </select> </div> </div> <div class="file-list-container"> <!-- 文件列表将通过AJAX加载 --> </div> <div class="file-pagination"> <!-- 分页控件 --> </div> </div> </div> </div> <?php } public function enqueue_scripts($hook) { if (strpos($hook, 'cloud-file') === false) { return; } wp_enqueue_style( 'cloud-file-manager', plugins_url('css/file-manager.css', __FILE__), [], '1.0.0' ); wp_enqueue_script( 'cloud-file-manager', plugins_url('js/file-manager.js', __FILE__), ['jquery', 'vue'], '1.0.0', true ); wp_localize_script('cloud-file-manager', 'cloudFileManager', [ 'ajax_url' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('cloud_file_manager'), 'strings' => [ 'delete_confirm' => '确定要删除这个文件吗?', 'upload_success' => '文件上传成功', 'upload_failed' => '文件上传失败', ] ]); } }
-
- <?php /** * 图片水印工具 */ class Image_Watermark_Tool { private $adapter; public function __construct($adapter) { $this->adapter = $adapter; add_action('wp_ajax_add_watermark', [$this, 'ajax_add_watermark']); } public function add_watermark($image_path, $watermark_text, $options = []) { // 从云存储下载图片 $temp_path = $this->download_to_temp($image_path); // 获取图片信息 $image_info = getimagesize($temp_path); $image_type = $image_info[2]; // 根据图片类型创建图像资源 switch ($image_type) { case IMAGETYPE_JPEG: $image = imagecreatefromjpeg($temp_path); break; case IMAGETYPE_PNG: $image = imagecreatefrompng($temp_path); break; case IMAGETYPE_GIF: $image = imagecreatefromgif($temp_path); break; default: return false; } // 设置水印颜色和字体 $text_color = imagecolorallocatealpha($image, 255, 255, 255, 60); $font_size = isset($options['font_size']) ? $options['font_size'] : 20; $font_path = isset($options['font_path']) ? $options['font_path'] : ''; // 计算水印位置 $text_box = imagettfbbox($font_size, 0, $font_path, $watermark_text); $text_width = $text_box[2] - $text_box[0]; $text_height = $text_box[7] - $text_box[1]; $x = imagesx($image) - $text_width - 10; $y = imagesy($image) - $text_height - 10; // 添加水印 imagettftext($image, $font_size, 0, $x, $y, $text_color, $font_path, $watermark_text); // 保存处理后的图片 $watermarked_path = $this->get_watermarked_path($temp_path); switch ($image_type) { case IMAGETYPE_JPEG: imagejpeg($image, $watermarked_path, 90); break; case IMAGETYPE_PNG: imagepng($image, $watermarked_path, 9); break; case IMAGETYPE_GIF: imagegif($image, $watermarked_path); break; } imagedestroy($image); // 上传处理后的图片 $new_remote_path = $this->get_watermarked_remote_path($image_path); $result = $this->adapter->upload($watermarked_path, $new_remote_path); // 清理临时文件 unlink($temp_path); unlink($watermarked_path); return $result['success'] ? $new_remote_path : false; } public function ajax_add_watermark() { check_ajax_referer('watermark_tool', 'nonce'); $file_id = intval($_POST['file_id']); $watermark_text = sanitize_text_field($_POST['watermark_text']); // 获取文件信息 $file_info = $this->get_file_info($file_id); if (!$file_info || !$this->is_image($file_info['mime_type'])) {
- <?php /** * 文件批量处理工具 */ class Batch_File_Processor { private $adapter; private $batch_size = 50; // 每批处理文件数量 public function __construct($adapter) { $this->adapter = $adapter; add_action('wp_ajax_batch_process_files', [$this, 'ajax_batch_process']); } public function process_batch($file_ids, $operation, $params = []) { $results = [ 'success' => [], 'failed' => [], 'total' => count($file_ids) ]; // 分批处理,避免内存溢出 $chunks = array_chunk($file_ids, $this->batch_size); foreach ($chunks as $chunk) { foreach ($chunk as $file_id) { try { $result = $this->process_single_file($file_id, $operation, $params); if ($result['success']) { $results['success'][] = [ 'id' => $file_id, 'message' => $result['message'] ]; } else { $results['failed'][] = [ 'id' => $file_id, 'error' => $result['error'] ]; } // 短暂暂停,减轻服务器压力 usleep(100000); // 0.1秒 } catch (Exception $e) { $results['failed'][] = [ 'id' => $file_id, 'error' => $e->getMessage() ]; } } } return $results; } private function process_single_file($file_id, $operation, $params) { global $wpdb; // 获取文件信息 $file = $wpdb->get_row($wpdb->prepare( "SELECT * FROM {$wpdb->prefix}cloud_files WHERE id = %d", $file_id )); if (!$file) { return ['success' => false, 'error' => '文件不存在']; } switch ($operation) { case 'compress_images': return $this->compress_image($file, $params); case 'convert_format': return $this->convert_format($file, $params); case 'add_metadata': return $this->add_metadata($file, $params); case 'generate_thumbnails': return $this->generate_thumbnails($file, $params); case 'optimize_for_web': return $this->optimize_for_web($file, $params); default: return ['success' => false, 'error' => '不支持的操作']; } } private function compress_image($file, $params) { if (!$this->is_image($file->mime_type)) { return ['success' => false, 'error' => '不是图片文件']; } // 下载文件到临时目录 $temp_path = $this->download_to_temp($file->remote_path); // 根据图片类型进行压缩 $compressed_path = $this->compress_image_file($temp_path, $params); if (!$compressed_path) { unlink($temp_path); return ['success' => false, 'error' => '压缩失败']; } // 计算压缩率 $original_size = filesize($temp_path); $compressed_size = filesize($compressed_path); $compression_rate = round((1 - $compressed_size / $original_size) * 100, 2); // 上传压缩后的文件 $new_remote_path = $this->get_compressed_path($file->remote_path); $upload_result = $this->adapter->upload($compressed_path, $new_remote_path); // 清理临时文件 unlink($temp_path); unlink($compressed_path); if ($upload_result['success']) { // 更新数据库记录 $this->update_file_record($file->id, [ 'compressed_path' => $new_remote_path, 'original_size' => $original_size, 'compressed_size' => $compressed_size, 'compression_rate' => $compression_rate ]); return [ 'success' => true, 'message' => "压缩成功,压缩率:{$compression_rate}%" ]; } return ['success' => false, 'error' => '上传压缩文件失败']; } private function compress_image_file($image_path, $params) { $quality = isset($params['quality']) ? $params['quality'] : 80; $max_width = isset($params['max_width']) ? $params['max_width'] : 1920; $image_info = getimagesize($image_path); $image_type = $image_info[2]; // 创建图像资源 switch ($image_type) { case IMAGETYPE_JPEG: $image = imagecreatefromjpeg($image_path); break; case IMAGETYPE_PNG: $image = imagecreatefrompng($image_path); // 保留透明度 imagealphablending($image, false); imagesavealpha($image, true); break; default: return false; } // 调整尺寸 $original_width = imagesx($image); $original_height = imagesy($image); if ($original_width > $max_width) { $new_width = $max_width; $new_height = intval($original_height * ($max_width / $original_width)); $resized_image = imagecreatetruecolor($new_width, $new_height); // 处理PNG透明度 if ($image_type == IMAGETYPE_PNG) { imagealphablending($resized_image, false); imagesavealpha($resized_image, true); $transparent = imagecolorallocatealpha($resized_image, 255, 255, 255, 127); imagefilledrectangle($resized_image, 0, 0, $new_width, $new_height, $transparent); } imagecopyresampled( $resized_image, $image, 0, 0, 0, 0, $new_width, $new_height, $original_width, $original_height ); imagedestroy($image); $image = $resized_image; } // 保存压缩后的图片 $compressed_path = $this->get_temp_file_path('compressed_'); switch ($image_type) { case IMAGETYPE_JPEG: imagejpeg($image, $compressed_path, $quality); break; case IMAGETYPE_PNG: // PNG质量参数是0-9,与JPEG相反 $png_quality = 9 - round(($quality / 100) * 9); imagepng($image, $compressed_path, $png_quality); break; } imagedestroy($image); return $compressed_path; } public function ajax_batch_process() { check_ajax_referer('batch_processor', 'nonce'); if (!current_user_can('upload_files')) { wp_send_json_error('权限不足'); } $file_ids = array_map('intval', $_POST['file_ids']); $operation = sanitize_text_field($_POST['operation']); $params = isset($_POST['params']) ? $_POST['params'] : []; // 异步处理 if (isset($_POST['async']) && $_POST['async']) { $this->start_async_batch_process($file_ids, $operation, $params); wp_send_json_success(['message' => '批量处理已开始']); } else { $results = $this->process_batch($file_ids, $operation, $params); wp_send_json_success($results); } } private function start_async_batch_process($file_ids, $operation, $params) { // 创建后台任务 $task_id = wp_generate_uuid4(); $task_data = [ 'file_ids' => $file_ids, 'operation' => $operation, 'params' => $params, 'status' => 'pending', 'progress' => 0, 'created_at' => current_time('mysql'), 'created_by' => get_current_user_id() ]; // 保存任务到数据库 $this->save_batch_task($task_id, $task_data); // 触发后台处理 wp_schedule_single_event(time() + 5, 'process_batch_task', [$task_id]); return $task_id; } }
- <?php /** * 智能文件分类器 */ class Smart_File_Classifier { private $adapter; private $categories = [ 'images' => ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'], 'documents' => ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt'], 'videos' => ['mp4', 'avi', 'mov', 'wmv', 'flv', 'mkv'], 'audio' => ['mp3', 'wav', 'ogg', 'm4a'], 'archives' => ['zip', 'rar', '7z', 'tar', 'gz'], 'code' => ['php', 'js', 'css', 'html', 'py', 'java', 'cpp'] ]; public function __construct($adapter) { $this->adapter = $adapter; add_action('wp_ajax_classify_files', [$this, 'ajax_classify_files']); add_action('add_attachment', [$this, 'auto_classify_new_file']); } public function classify_file($file_path, $file_name) { $extension = strtolower(pathinfo($file_name, PATHINFO_EXTENSION)); // 根据扩展名分类 foreach ($this->categories as $category => $extensions) { if (in_array($extension, $extensions)) { return $category; } } // 使用MIME类型进一步分类 $mime_type = $this->get_mime_type($file_path); if ($mime_type) { return $this->classify_by_mime_type($mime_type); } return 'other'; } private function classify_by_mime_type($mime_type) { $mime_categories = [ 'image/' => 'images', 'application/pdf' => 'documents', 'application/msword' => 'documents', 'application/vnd.openxmlformats-officedocument' => 'documents', 'video/' => 'videos', 'audio/' => 'audio', 'application/zip' => 'archives', 'application/x-rar-compressed' => 'archives', 'text/' => 'documents' ]; foreach ($mime_categories as $prefix => $category) { if (strpos($mime_type, $prefix) === 0) { return $category; } } return 'other'; } public function auto_classify_new_file($attachment_id) { $file_path = get_attached_file($attachment_id); $file_name = basename($file_path); $category = $this->classify_file($file_path, $file_name); // 保存分类信息 update_post_meta($attachment_id, '_file_category', $category); // 如果是图片,提取更多信息 if ($category === 'images') { $this->extract_image_metadata($attachment_id, $file_path); } } private function extract_image_metadata($attachment_id, $file_path) { $metadata = []; // 获取EXIF数据 if (function_exists('exif_read_data') && in_array(strtolower(pathinfo($file_path, PATHINFO_EXTENSION)), ['jpg', 'jpeg'])) { $exif = @exif_read_data($file_path); if ($exif) { if (isset($exif['DateTimeOriginal'])) { $metadata['taken_date'] = $exif['DateTimeOriginal']; } if (isset($exif['GPSLatitude']) && isset($exif['GPSLongitude'])) { $metadata['gps'] = $this->convert_gps($exif['GPSLatitude'], $exif['GPSLongitude']); } if (isset($exif['Make'])) { $metadata['camera_make'] = $exif['Make']; } if (isset($exif['Model'])) { $metadata['camera_model'] = $exif['Model']; } } } // 获取图片尺寸 $image_size = getimagesize($file_path); if ($image_size) { $metadata['dimensions'] = [ 'width' => $image_size[0], 'height' => $image_size[1] ]; $metadata['mime_type'] = $image_size['mime']; } // 计算文件哈希 $metadata['file_hash'] = md5_file($file_path); // 保存元数据 update_post_meta($attachment_id, '_image_metadata', $metadata); } private function convert_gps($gps_lat, $gps_lon) { // 将GPS坐标转换为十进制 $lat_degrees = count($gps_lat) > 0 ? $this->gps_to_degrees($gps_lat) : 0; $lon_degrees = count($gps_lon) > 0 ? $this->gps_to_degrees($gps_lon) : 0; // 确定半球 $lat_direction = ($gps_lat['GPSLatitudeRef'] == 'S') ? -1 : 1; $lon_direction = ($gps_lon['GPSLongitudeRef'] == 'W') ? -1 : 1; return [ 'lat' => $lat_degrees * $lat_direction, 'lon' => $lon_degrees * $lon_direction ]; } private function gps_to_degrees($gps_coordinate) { $degrees = count($gps_coordinate) > 0 ? $this->gps_coordinate_to_number($gps_coordinate[0]) : 0; $minutes = count($gps_coordinate) > 1 ? $this->gps_coordinate_to_number($gps_coordinate[1]) : 0; $seconds = count($gps_coordinate) > 2 ? $this->gps_coordinate_to_number($gps_coordinate[2]) : 0; return $degrees + ($minutes / 60) + ($seconds / 3600); } private function gps_coordinate_to_number($coordinate_part) { $parts = explode('/', $coordinate_part); if (count($parts) <= 0) { return 0; } if (count($parts) == 1) { return $parts[0]; } return floatval($parts[0]) / floatval($parts[1]); } public function ajax_classify_files() { check_ajax_referer('file_classifier', 'nonce'); $file_ids = array_map('intval', $_POST['file_ids']); $results = []; foreach ($file_ids as $file_id) { $file = get_post($file_id); if (!$file || $file->post_type != 'attachment') { continue; } $file_path = get_attached_file($file_id); $category = $this->classify_file($file_path, $file->post_title); // 更新分类 update_post_meta($file_id, '_file_category', $category); $results[] = [ 'id' => $file_id, 'name' => $file->post_title, 'category' => $category, 'icon' => $this->get_category_icon($category) ]; } wp_send_json_success([ 'results' => $results, 'total' => count($results) ]); } private function get_category_icon($category) { $icons = [ 'images' => 'dashicons-format-image', 'documents' => 'dashicons-media-document', 'videos' => 'dashicons-format-video', 'audio' => 'dashicons-format-audio', 'archives' => 'dashicons-media-archive', 'code' => 'dashicons-editor-code', 'other' => 'dashicons-media-default' ]; return isset($icons[$category]) ? $icons[$category] : $icons['other']; } }
- <?php /** * 文件分享与协作工具 */ class File_Sharing_Tool { private $adapter; private $share_expiry_days = 7; public function __construct($adapter) { $this->adapter = $adapter; add_action('wp_ajax_create_file_share', [$this, 'ajax_create_share']); add_action('wp_ajax_revoke_file_share', [$this, 'ajax_revoke_share']); add_action('wp', [$this, 'handle_shared_file_access']); } public function create_share_link($file_id, $options = []) { global $wpdb; // 验证文件存在且用户有权限 $file = $wpdb->get_row($wpdb->prepare( "SELECT * FROM {$wpdb->prefix}cloud_files WHERE id = %d", $file_id )); if (!$file) {
在当今数字化时代,网站的功能性需求日益复杂。无论是企业官网、个人博客,还是电子商务平台,文件上传与管理功能已成为不可或缺的基础需求。用户期望能够轻松上传图片、文档、视频等内容,而网站管理员则需要高效、安全地管理这些文件资源。
传统的WordPress媒体库虽然提供了基础的文件上传功能,但在面对大量文件、大文件上传、多用户协作、跨平台访问等复杂场景时,往往显得力不从心。云存储解决方案的出现,为这一问题提供了完美的答案。通过将文件存储在云端,网站不仅可以减轻服务器负担,还能实现更高的可用性、可扩展性和安全性。
本文将深入探讨如何通过WordPress代码二次开发,为网站添加强大的文件上传与云存储管理功能,并在此基础上实现一系列常用互联网小工具,从而大幅提升网站的功能性和用户体验。
WordPress内置了一个相对完整的文件上传系统。当用户通过媒体上传界面或相关功能上传文件时,系统会执行以下关键步骤:
- 文件验证:检查文件类型、大小是否符合系统设置
- 安全处理:对文件名进行清理,防止安全漏洞
- 存储处理:将文件保存到wp-content/uploads目录,并按年月组织子目录
- 数据库记录:在wp_posts表中创建attachment类型的记录
- 元数据生成:为图片文件生成缩略图,提取EXIF信息等
了解这一基础流程是进行二次开发的前提。我们可以通过分析wp_handle_upload()、media_handle_upload()等核心函数,掌握WordPress处理上传文件的完整机制。
尽管WordPress默认系统能够满足基本需求,但在实际应用中存在明显不足:
- 存储空间受限:依赖服务器本地存储,空间有限且不易扩展
- 性能瓶颈:大文件上传和处理可能拖慢网站响应速度
- 备份困难:文件分散在服务器上,难以实现统一备份和恢复
- 访问限制:缺乏细粒度的访问控制和权限管理
- 多站点管理复杂:对于多站点网络,文件管理变得异常复杂
这些局限性正是我们需要引入云存储解决方案的根本原因。
目前市场上有多种云存储服务可供选择,每种都有其特点和适用场景:
- Amazon S3:功能全面,生态系统完善,适合企业级应用
- Google Cloud Storage:与Google生态系统深度集成,性能优异
- 阿里云OSS:国内访问速度快,符合中国法规要求
- 腾讯云COS:性价比高,与腾讯生态整合良好
- Backblaze B2:价格实惠,适合个人和小型企业
- Wasabi:无出口费用,适合高频访问场景
在选择云存储服务时,需要综合考虑以下因素:
- 成本结构:存储费用、请求费用、流量费用的综合评估
- 性能表现:上传下载速度、延迟、可用性保证
- 地理位置:服务器位置对访问速度的影响
- 集成难度:API的易用性和文档完整性
- 合规要求:数据主权、隐私保护等法规遵从性
- 生态系统:与其他服务的集成能力
在开始自定义开发前,了解现有插件解决方案是必要的:
- WP Offload Media:功能全面,支持多种云服务
- Media Cloud:专注于云存储,提供高级功能
- Stateless:与Google Cloud Platform深度集成
- Storage for WordPress:轻量级解决方案,易于定制
虽然这些插件提供了现成的解决方案,但通过自定义开发,我们可以实现更贴合特定需求、更高效集成的文件管理系统。
我们的自定义系统将采用模块化设计,主要包括以下组件:
- 上传处理模块:负责接收、验证和处理上传请求
- 云存储适配器:抽象不同云服务的API,提供统一接口
- 文件管理模块:提供文件的增删改查操作
- 权限控制系统:管理用户对文件的访问权限
- 缓存与优化层:提高系统性能和响应速度
- 管理界面:为管理员和用户提供友好的操作界面
<?php
/**
* 云存储适配器抽象类
*/
abstract class Cloud_Storage_Adapter {
protected $config;
protected $client;
public function __construct($config) {
$this->config = $config;
$this->initialize_client();
}
abstract protected function initialize_client();
abstract public function upload($local_path, $remote_path, $options = []);
abstract public function download($remote_path, $local_path);
abstract public function delete($remote_path);
abstract public function list_files($prefix = '', $options = []);
abstract public function get_url($remote_path, $expires = null);
abstract public function file_exists($remote_path);
}
<?php
/**
* 云存储适配器抽象类
*/
abstract class Cloud_Storage_Adapter {
protected $config;
protected $client;
public function __construct($config) {
$this->config = $config;
$this->initialize_client();
}
abstract protected function initialize_client();
abstract public function upload($local_path, $remote_path, $options = []);
abstract public function download($remote_path, $local_path);
abstract public function delete($remote_path);
abstract public function list_files($prefix = '', $options = []);
abstract public function get_url($remote_path, $expires = null);
abstract public function file_exists($remote_path);
}
<?php
/**
* Amazon S3适配器实现
*/
class S3_Storage_Adapter extends Cloud_Storage_Adapter {
protected function initialize_client() {
$this->client = new AwsS3S3Client([
'version' => 'latest',
'region' => $this->config['region'],
'credentials' => [
'key' => $this->config['key'],
'secret' => $this->config['secret'],
],
]);
}
public function upload($local_path, $remote_path, $options = []) {
$default_options = [
'Bucket' => $this->config['bucket'],
'Key' => $remote_path,
'SourceFile' => $local_path,
'ACL' => 'private',
];
$options = array_merge($default_options, $options);
try {
$result = $this->client->putObject($options);
return [
'success' => true,
'url' => $result['ObjectURL'],
'etag' => $result['ETag'],
];
} catch (AwsS3ExceptionS3Exception $e) {
return [
'success' => false,
'error' => $e->getMessage(),
];
}
}
// 其他方法实现...
}
<?php
/**
* Amazon S3适配器实现
*/
class S3_Storage_Adapter extends Cloud_Storage_Adapter {
protected function initialize_client() {
$this->client = new AwsS3S3Client([
'version' => 'latest',
'region' => $this->config['region'],
'credentials' => [
'key' => $this->config['key'],
'secret' => $this->config['secret'],
],
]);
}
public function upload($local_path, $remote_path, $options = []) {
$default_options = [
'Bucket' => $this->config['bucket'],
'Key' => $remote_path,
'SourceFile' => $local_path,
'ACL' => 'private',
];
$options = array_merge($default_options, $options);
try {
$result = $this->client->putObject($options);
return [
'success' => true,
'url' => $result['ObjectURL'],
'etag' => $result['ETag'],
];
} catch (AwsS3ExceptionS3Exception $e) {
return [
'success' => false,
'error' => $e->getMessage(),
];
}
}
// 其他方法实现...
}
<?php
/**
* 文件上传处理器
*/
class File_Upload_Handler {
private $adapter;
private $allowed_types;
private $max_size;
public function __construct($adapter) {
$this->adapter = $adapter;
$this->allowed_types = get_option('allowed_upload_types', []);
$this->max_size = get_option('max_upload_size', 10485760); // 默认10MB
}
public function handle_upload($file, $user_id, $options = []) {
// 验证文件
$validation = $this->validate_file($file);
if (!$validation['valid']) {
return $validation;
}
// 生成唯一文件名和路径
$file_info = $this->generate_file_info($file, $user_id);
// 临时保存文件
$temp_path = $this->save_temp_file($file);
// 上传到云存储
$upload_result = $this->adapter->upload(
$temp_path,
$file_info['remote_path'],
$options
);
// 清理临时文件
unlink($temp_path);
if ($upload_result['success']) {
// 保存文件记录到数据库
$file_id = $this->save_file_record($file_info, $user_id);
return [
'success' => true,
'file_id' => $file_id,
'url' => $upload_result['url'],
'file_info' => $file_info,
];
}
return [
'success' => false,
'error' => $upload_result['error'],
];
}
private function validate_file($file) {
// 检查文件大小
if ($file['size'] > $this->max_size) {
return [
'valid' => false,
'error' => '文件大小超过限制',
];
}
// 检查文件类型
$file_type = wp_check_filetype($file['name']);
if (!in_array($file_type['type'], $this->allowed_types)) {
return [
'valid' => false,
'error' => '不支持的文件类型',
];
}
// 安全检查
if (!wp_verify_nonce($_POST['upload_nonce'], 'file_upload')) {
return [
'valid' => false,
'error' => '安全验证失败',
];
}
return ['valid' => true];
}
private function generate_file_info($file, $user_id) {
$original_name = sanitize_file_name($file['name']);
$extension = pathinfo($original_name, PATHINFO_EXTENSION);
$unique_name = wp_generate_uuid4() . '.' . $extension;
// 按用户和日期组织目录结构
$date = date('Y/m');
$remote_path = "uploads/{$user_id}/{$date}/{$unique_name}";
return [
'original_name' => $original_name,
'unique_name' => $unique_name,
'remote_path' => $remote_path,
'extension' => $extension,
'size' => $file['size'],
];
}
// 其他辅助方法...
}
<?php
/**
* 文件上传处理器
*/
class File_Upload_Handler {
private $adapter;
private $allowed_types;
private $max_size;
public function __construct($adapter) {
$this->adapter = $adapter;
$this->allowed_types = get_option('allowed_upload_types', []);
$this->max_size = get_option('max_upload_size', 10485760); // 默认10MB
}
public function handle_upload($file, $user_id, $options = []) {
// 验证文件
$validation = $this->validate_file($file);
if (!$validation['valid']) {
return $validation;
}
// 生成唯一文件名和路径
$file_info = $this->generate_file_info($file, $user_id);
// 临时保存文件
$temp_path = $this->save_temp_file($file);
// 上传到云存储
$upload_result = $this->adapter->upload(
$temp_path,
$file_info['remote_path'],
$options
);
// 清理临时文件
unlink($temp_path);
if ($upload_result['success']) {
// 保存文件记录到数据库
$file_id = $this->save_file_record($file_info, $user_id);
return [
'success' => true,
'file_id' => $file_id,
'url' => $upload_result['url'],
'file_info' => $file_info,
];
}
return [
'success' => false,
'error' => $upload_result['error'],
];
}
private function validate_file($file) {
// 检查文件大小
if ($file['size'] > $this->max_size) {
return [
'valid' => false,
'error' => '文件大小超过限制',
];
}
// 检查文件类型
$file_type = wp_check_filetype($file['name']);
if (!in_array($file_type['type'], $this->allowed_types)) {
return [
'valid' => false,
'error' => '不支持的文件类型',
];
}
// 安全检查
if (!wp_verify_nonce($_POST['upload_nonce'], 'file_upload')) {
return [
'valid' => false,
'error' => '安全验证失败',
];
}
return ['valid' => true];
}
private function generate_file_info($file, $user_id) {
$original_name = sanitize_file_name($file['name']);
$extension = pathinfo($original_name, PATHINFO_EXTENSION);
$unique_name = wp_generate_uuid4() . '.' . $extension;
// 按用户和日期组织目录结构
$date = date('Y/m');
$remote_path = "uploads/{$user_id}/{$date}/{$unique_name}";
return [
'original_name' => $original_name,
'unique_name' => $unique_name,
'remote_path' => $remote_path,
'extension' => $extension,
'size' => $file['size'],
];
}
// 其他辅助方法...
}
为了高效管理文件元数据,我们需要创建自定义数据库表:
CREATE TABLE wp_cloud_files (
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
user_id BIGINT(20) UNSIGNED NOT NULL,
original_name VARCHAR(255) NOT NULL,
unique_name VARCHAR(255) NOT NULL,
remote_path VARCHAR(500) NOT NULL,
file_type VARCHAR(100) NOT NULL,
file_size BIGINT(20) UNSIGNED NOT NULL,
mime_type VARCHAR(100),
upload_time DATETIME DEFAULT CURRENT_TIMESTAMP,
last_access DATETIME,
access_count INT UNSIGNED DEFAULT 0,
is_public TINYINT(1) DEFAULT 0,
metadata TEXT,
PRIMARY KEY (id),
INDEX user_index (user_id),
INDEX path_index (remote_path(255)),
INDEX type_index (file_type),
INDEX upload_time_index (upload_time)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
创建用户友好的管理界面是系统成功的关键。我们可以使用WordPress的Admin API创建自定义管理页面:
<?php
/**
* 文件管理界面
*/
class File_Management_UI {
public function __construct() {
add_action('admin_menu', [$this, 'add_admin_menu']);
add_action('admin_enqueue_scripts', [$this, 'enqueue_scripts']);
}
public function add_admin_menu() {
add_menu_page(
'云文件管理',
'云文件',
'upload_files',
'cloud-file-manager',
[$this, 'render_main_page'],
'dashicons-cloud',
30
);
add_submenu_page(
'cloud-file-manager',
'上传文件',
'上传',
'upload_files',
'cloud-file-upload',
[$this, 'render_upload_page']
);
add_submenu_page(
'cloud-file-manager',
'文件统计',
'统计',
'manage_options',
'cloud-file-stats',
[$this, 'render_stats_page']
);
}
public function render_main_page() {
?>
<div class="wrap">
<h1 class="wp-heading-inline">云文件管理</h1>
<a href="<?php echo admin_url('admin.php?page=cloud-file-upload'); ?>" class="page-title-action">上传文件</a>
<hr class="wp-header-end">
<div id="cloud-file-manager">
<!-- 文件列表将通过Vue.js动态加载 -->
<div class="file-manager-container">
<div class="file-toolbar">
<div class="search-box">
<input type="search" id="file-search" placeholder="搜索文件...">
</div>
<div class="filter-options">
<select id="file-type-filter">
<option value="">所有类型</option>
<option value="image">图片</option>
<option value="document">文档</option>
<option value="video">视频</option>
</select>
</div>
</div>
<div class="file-list-container">
<!-- 文件列表将通过AJAX加载 -->
</div>
<div class="file-pagination">
<!-- 分页控件 -->
</div>
</div>
</div>
</div>
<?php
}
public function enqueue_scripts($hook) {
if (strpos($hook, 'cloud-file') === false) {
return;
}
wp_enqueue_style(
'cloud-file-manager',
plugins_url('css/file-manager.css', __FILE__),
[],
'1.0.0'
);
wp_enqueue_script(
'cloud-file-manager',
plugins_url('js/file-manager.js', __FILE__),
['jquery', 'vue'],
'1.0.0',
true
);
wp_localize_script('cloud-file-manager', 'cloudFileManager', [
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('cloud_file_manager'),
'strings' => [
'delete_confirm' => '确定要删除这个文件吗?',
'upload_success' => '文件上传成功',
'upload_failed' => '文件上传失败',
]
]);
}
}
<?php
/**
* 图片水印工具
*/
class Image_Watermark_Tool {
private $adapter;
public function __construct($adapter) {
$this->adapter = $adapter;
add_action('wp_ajax_add_watermark', [$this, 'ajax_add_watermark']);
}
public function add_watermark($image_path, $watermark_text, $options = []) {
// 从云存储下载图片
$temp_path = $this->download_to_temp($image_path);
// 获取图片信息
$image_info = getimagesize($temp_path);
$image_type = $image_info[2];
// 根据图片类型创建图像资源
switch ($image_type) {
case IMAGETYPE_JPEG:
$image = imagecreatefromjpeg($temp_path);
break;
case IMAGETYPE_PNG:
$image = imagecreatefrompng($temp_path);
break;
case IMAGETYPE_GIF:
$image = imagecreatefromgif($temp_path);
break;
default:
return false;
}
// 设置水印颜色和字体
$text_color = imagecolorallocatealpha($image, 255, 255, 255, 60);
$font_size = isset($options['font_size']) ? $options['font_size'] : 20;
$font_path = isset($options['font_path']) ? $options['font_path'] : '';
// 计算水印位置
$text_box = imagettfbbox($font_size, 0, $font_path, $watermark_text);
$text_width = $text_box[2] - $text_box[0];
$text_height = $text_box[7] - $text_box[1];
$x = imagesx($image) - $text_width - 10;
$y = imagesy($image) - $text_height - 10;
// 添加水印
imagettftext($image, $font_size, 0, $x, $y, $text_color, $font_path, $watermark_text);
// 保存处理后的图片
$watermarked_path = $this->get_watermarked_path($temp_path);
switch ($image_type) {
case IMAGETYPE_JPEG:
imagejpeg($image, $watermarked_path, 90);
break;
case IMAGETYPE_PNG:
imagepng($image, $watermarked_path, 9);
break;
case IMAGETYPE_GIF:
imagegif($image, $watermarked_path);
break;
}
imagedestroy($image);
// 上传处理后的图片
$new_remote_path = $this->get_watermarked_remote_path($image_path);
$result = $this->adapter->upload($watermarked_path, $new_remote_path);
// 清理临时文件
unlink($temp_path);
unlink($watermarked_path);
return $result['success'] ? $new_remote_path : false;
}
public function ajax_add_watermark() {
check_ajax_referer('watermark_tool', 'nonce');
$file_id = intval($_POST['file_id']);
$watermark_text = sanitize_text_field($_POST['watermark_text']);
// 获取文件信息
$file_info = $this->get_file_info($file_id);
if (!$file_info || !$this->is_image($file_info['mime_type'])) {
<?php
/**
* 图片水印工具
*/
class Image_Watermark_Tool {
private $adapter;
public function __construct($adapter) {
$this->adapter = $adapter;
add_action('wp_ajax_add_watermark', [$this, 'ajax_add_watermark']);
}
public function add_watermark($image_path, $watermark_text, $options = []) {
// 从云存储下载图片
$temp_path = $this->download_to_temp($image_path);
// 获取图片信息
$image_info = getimagesize($temp_path);
$image_type = $image_info[2];
// 根据图片类型创建图像资源
switch ($image_type) {
case IMAGETYPE_JPEG:
$image = imagecreatefromjpeg($temp_path);
break;
case IMAGETYPE_PNG:
$image = imagecreatefrompng($temp_path);
break;
case IMAGETYPE_GIF:
$image = imagecreatefromgif($temp_path);
break;
default:
return false;
}
// 设置水印颜色和字体
$text_color = imagecolorallocatealpha($image, 255, 255, 255, 60);
$font_size = isset($options['font_size']) ? $options['font_size'] : 20;
$font_path = isset($options['font_path']) ? $options['font_path'] : '';
// 计算水印位置
$text_box = imagettfbbox($font_size, 0, $font_path, $watermark_text);
$text_width = $text_box[2] - $text_box[0];
$text_height = $text_box[7] - $text_box[1];
$x = imagesx($image) - $text_width - 10;
$y = imagesy($image) - $text_height - 10;
// 添加水印
imagettftext($image, $font_size, 0, $x, $y, $text_color, $font_path, $watermark_text);
// 保存处理后的图片
$watermarked_path = $this->get_watermarked_path($temp_path);
switch ($image_type) {
case IMAGETYPE_JPEG:
imagejpeg($image, $watermarked_path, 90);
break;
case IMAGETYPE_PNG:
imagepng($image, $watermarked_path, 9);
break;
case IMAGETYPE_GIF:
imagegif($image, $watermarked_path);
break;
}
imagedestroy($image);
// 上传处理后的图片
$new_remote_path = $this->get_watermarked_remote_path($image_path);
$result = $this->adapter->upload($watermarked_path, $new_remote_path);
// 清理临时文件
unlink($temp_path);
unlink($watermarked_path);
return $result['success'] ? $new_remote_path : false;
}
public function ajax_add_watermark() {
check_ajax_referer('watermark_tool', 'nonce');
$file_id = intval($_POST['file_id']);
$watermark_text = sanitize_text_field($_POST['watermark_text']);
// 获取文件信息
$file_info = $this->get_file_info($file_id);
if (!$file_info || !$this->is_image($file_info['mime_type'])) {
<?php
/**
* 文件批量处理工具
*/
class Batch_File_Processor {
private $adapter;
private $batch_size = 50; // 每批处理文件数量
public function __construct($adapter) {
$this->adapter = $adapter;
add_action('wp_ajax_batch_process_files', [$this, 'ajax_batch_process']);
}
public function process_batch($file_ids, $operation, $params = []) {
$results = [
'success' => [],
'failed' => [],
'total' => count($file_ids)
];
// 分批处理,避免内存溢出
$chunks = array_chunk($file_ids, $this->batch_size);
foreach ($chunks as $chunk) {
foreach ($chunk as $file_id) {
try {
$result = $this->process_single_file($file_id, $operation, $params);
if ($result['success']) {
$results['success'][] = [
'id' => $file_id,
'message' => $result['message']
];
} else {
$results['failed'][] = [
'id' => $file_id,
'error' => $result['error']
];
}
// 短暂暂停,减轻服务器压力
usleep(100000); // 0.1秒
} catch (Exception $e) {
$results['failed'][] = [
'id' => $file_id,
'error' => $e->getMessage()
];
}
}
}
return $results;
}
private function process_single_file($file_id, $operation, $params) {
global $wpdb;
// 获取文件信息
$file = $wpdb->get_row($wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}cloud_files WHERE id = %d",
$file_id
));
if (!$file) {
return ['success' => false, 'error' => '文件不存在'];
}
switch ($operation) {
case 'compress_images':
return $this->compress_image($file, $params);
case 'convert_format':
return $this->convert_format($file, $params);
case 'add_metadata':
return $this->add_metadata($file, $params);
case 'generate_thumbnails':
return $this->generate_thumbnails($file, $params);
case 'optimize_for_web':
return $this->optimize_for_web($file, $params);
default:
return ['success' => false, 'error' => '不支持的操作'];
}
}
private function compress_image($file, $params) {
if (!$this->is_image($file->mime_type)) {
return ['success' => false, 'error' => '不是图片文件'];
}
// 下载文件到临时目录
$temp_path = $this->download_to_temp($file->remote_path);
// 根据图片类型进行压缩
$compressed_path = $this->compress_image_file($temp_path, $params);
if (!$compressed_path) {
unlink($temp_path);
return ['success' => false, 'error' => '压缩失败'];
}
// 计算压缩率
$original_size = filesize($temp_path);
$compressed_size = filesize($compressed_path);
$compression_rate = round((1 - $compressed_size / $original_size) * 100, 2);
// 上传压缩后的文件
$new_remote_path = $this->get_compressed_path($file->remote_path);
$upload_result = $this->adapter->upload($compressed_path, $new_remote_path);
// 清理临时文件
unlink($temp_path);
unlink($compressed_path);
if ($upload_result['success']) {
// 更新数据库记录
$this->update_file_record($file->id, [
'compressed_path' => $new_remote_path,
'original_size' => $original_size,
'compressed_size' => $compressed_size,
'compression_rate' => $compression_rate
]);
return [
'success' => true,
'message' => "压缩成功,压缩率:{$compression_rate}%"
];
}
return ['success' => false, 'error' => '上传压缩文件失败'];
}
private function compress_image_file($image_path, $params) {
$quality = isset($params['quality']) ? $params['quality'] : 80;
$max_width = isset($params['max_width']) ? $params['max_width'] : 1920;
$image_info = getimagesize($image_path);
$image_type = $image_info[2];
// 创建图像资源
switch ($image_type) {
case IMAGETYPE_JPEG:
$image = imagecreatefromjpeg($image_path);
break;
case IMAGETYPE_PNG:
$image = imagecreatefrompng($image_path);
// 保留透明度
imagealphablending($image, false);
imagesavealpha($image, true);
break;
default:
return false;
}
// 调整尺寸
$original_width = imagesx($image);
$original_height = imagesy($image);
if ($original_width > $max_width) {
$new_width = $max_width;
$new_height = intval($original_height * ($max_width / $original_width));
$resized_image = imagecreatetruecolor($new_width, $new_height);
// 处理PNG透明度
if ($image_type == IMAGETYPE_PNG) {
imagealphablending($resized_image, false);
imagesavealpha($resized_image, true);
$transparent = imagecolorallocatealpha($resized_image, 255, 255, 255, 127);
imagefilledrectangle($resized_image, 0, 0, $new_width, $new_height, $transparent);
}
imagecopyresampled(
$resized_image, $image,
0, 0, 0, 0,
$new_width, $new_height,
$original_width, $original_height
);
imagedestroy($image);
$image = $resized_image;
}
// 保存压缩后的图片
$compressed_path = $this->get_temp_file_path('compressed_');
switch ($image_type) {
case IMAGETYPE_JPEG:
imagejpeg($image, $compressed_path, $quality);
break;
case IMAGETYPE_PNG:
// PNG质量参数是0-9,与JPEG相反
$png_quality = 9 - round(($quality / 100) * 9);
imagepng($image, $compressed_path, $png_quality);
break;
}
imagedestroy($image);
return $compressed_path;
}
public function ajax_batch_process() {
check_ajax_referer('batch_processor', 'nonce');
if (!current_user_can('upload_files')) {
wp_send_json_error('权限不足');
}
$file_ids = array_map('intval', $_POST['file_ids']);
$operation = sanitize_text_field($_POST['operation']);
$params = isset($_POST['params']) ? $_POST['params'] : [];
// 异步处理
if (isset($_POST['async']) && $_POST['async']) {
$this->start_async_batch_process($file_ids, $operation, $params);
wp_send_json_success(['message' => '批量处理已开始']);
} else {
$results = $this->process_batch($file_ids, $operation, $params);
wp_send_json_success($results);
}
}
private function start_async_batch_process($file_ids, $operation, $params) {
// 创建后台任务
$task_id = wp_generate_uuid4();
$task_data = [
'file_ids' => $file_ids,
'operation' => $operation,
'params' => $params,
'status' => 'pending',
'progress' => 0,
'created_at' => current_time('mysql'),
'created_by' => get_current_user_id()
];
// 保存任务到数据库
$this->save_batch_task($task_id, $task_data);
// 触发后台处理
wp_schedule_single_event(time() + 5, 'process_batch_task', [$task_id]);
return $task_id;
}
}
<?php
/**
* 文件批量处理工具
*/
class Batch_File_Processor {
private $adapter;
private $batch_size = 50; // 每批处理文件数量
public function __construct($adapter) {
$this->adapter = $adapter;
add_action('wp_ajax_batch_process_files', [$this, 'ajax_batch_process']);
}
public function process_batch($file_ids, $operation, $params = []) {
$results = [
'success' => [],
'failed' => [],
'total' => count($file_ids)
];
// 分批处理,避免内存溢出
$chunks = array_chunk($file_ids, $this->batch_size);
foreach ($chunks as $chunk) {
foreach ($chunk as $file_id) {
try {
$result = $this->process_single_file($file_id, $operation, $params);
if ($result['success']) {
$results['success'][] = [
'id' => $file_id,
'message' => $result['message']
];
} else {
$results['failed'][] = [
'id' => $file_id,
'error' => $result['error']
];
}
// 短暂暂停,减轻服务器压力
usleep(100000); // 0.1秒
} catch (Exception $e) {
$results['failed'][] = [
'id' => $file_id,
'error' => $e->getMessage()
];
}
}
}
return $results;
}
private function process_single_file($file_id, $operation, $params) {
global $wpdb;
// 获取文件信息
$file = $wpdb->get_row($wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}cloud_files WHERE id = %d",
$file_id
));
if (!$file) {
return ['success' => false, 'error' => '文件不存在'];
}
switch ($operation) {
case 'compress_images':
return $this->compress_image($file, $params);
case 'convert_format':
return $this->convert_format($file, $params);
case 'add_metadata':
return $this->add_metadata($file, $params);
case 'generate_thumbnails':
return $this->generate_thumbnails($file, $params);
case 'optimize_for_web':
return $this->optimize_for_web($file, $params);
default:
return ['success' => false, 'error' => '不支持的操作'];
}
}
private function compress_image($file, $params) {
if (!$this->is_image($file->mime_type)) {
return ['success' => false, 'error' => '不是图片文件'];
}
// 下载文件到临时目录
$temp_path = $this->download_to_temp($file->remote_path);
// 根据图片类型进行压缩
$compressed_path = $this->compress_image_file($temp_path, $params);
if (!$compressed_path) {
unlink($temp_path);
return ['success' => false, 'error' => '压缩失败'];
}
// 计算压缩率
$original_size = filesize($temp_path);
$compressed_size = filesize($compressed_path);
$compression_rate = round((1 - $compressed_size / $original_size) * 100, 2);
// 上传压缩后的文件
$new_remote_path = $this->get_compressed_path($file->remote_path);
$upload_result = $this->adapter->upload($compressed_path, $new_remote_path);
// 清理临时文件
unlink($temp_path);
unlink($compressed_path);
if ($upload_result['success']) {
// 更新数据库记录
$this->update_file_record($file->id, [
'compressed_path' => $new_remote_path,
'original_size' => $original_size,
'compressed_size' => $compressed_size,
'compression_rate' => $compression_rate
]);
return [
'success' => true,
'message' => "压缩成功,压缩率:{$compression_rate}%"
];
}
return ['success' => false, 'error' => '上传压缩文件失败'];
}
private function compress_image_file($image_path, $params) {
$quality = isset($params['quality']) ? $params['quality'] : 80;
$max_width = isset($params['max_width']) ? $params['max_width'] : 1920;
$image_info = getimagesize($image_path);
$image_type = $image_info[2];
// 创建图像资源
switch ($image_type) {
case IMAGETYPE_JPEG:
$image = imagecreatefromjpeg($image_path);
break;
case IMAGETYPE_PNG:
$image = imagecreatefrompng($image_path);
// 保留透明度
imagealphablending($image, false);
imagesavealpha($image, true);
break;
default:
return false;
}
// 调整尺寸
$original_width = imagesx($image);
$original_height = imagesy($image);
if ($original_width > $max_width) {
$new_width = $max_width;
$new_height = intval($original_height * ($max_width / $original_width));
$resized_image = imagecreatetruecolor($new_width, $new_height);
// 处理PNG透明度
if ($image_type == IMAGETYPE_PNG) {
imagealphablending($resized_image, false);
imagesavealpha($resized_image, true);
$transparent = imagecolorallocatealpha($resized_image, 255, 255, 255, 127);
imagefilledrectangle($resized_image, 0, 0, $new_width, $new_height, $transparent);
}
imagecopyresampled(
$resized_image, $image,
0, 0, 0, 0,
$new_width, $new_height,
$original_width, $original_height
);
imagedestroy($image);
$image = $resized_image;
}
// 保存压缩后的图片
$compressed_path = $this->get_temp_file_path('compressed_');
switch ($image_type) {
case IMAGETYPE_JPEG:
imagejpeg($image, $compressed_path, $quality);
break;
case IMAGETYPE_PNG:
// PNG质量参数是0-9,与JPEG相反
$png_quality = 9 - round(($quality / 100) * 9);
imagepng($image, $compressed_path, $png_quality);
break;
}
imagedestroy($image);
return $compressed_path;
}
public function ajax_batch_process() {
check_ajax_referer('batch_processor', 'nonce');
if (!current_user_can('upload_files')) {
wp_send_json_error('权限不足');
}
$file_ids = array_map('intval', $_POST['file_ids']);
$operation = sanitize_text_field($_POST['operation']);
$params = isset($_POST['params']) ? $_POST['params'] : [];
// 异步处理
if (isset($_POST['async']) && $_POST['async']) {
$this->start_async_batch_process($file_ids, $operation, $params);
wp_send_json_success(['message' => '批量处理已开始']);
} else {
$results = $this->process_batch($file_ids, $operation, $params);
wp_send_json_success($results);
}
}
private function start_async_batch_process($file_ids, $operation, $params) {
// 创建后台任务
$task_id = wp_generate_uuid4();
$task_data = [
'file_ids' => $file_ids,
'operation' => $operation,
'params' => $params,
'status' => 'pending',
'progress' => 0,
'created_at' => current_time('mysql'),
'created_by' => get_current_user_id()
];
// 保存任务到数据库
$this->save_batch_task($task_id, $task_data);
// 触发后台处理
wp_schedule_single_event(time() + 5, 'process_batch_task', [$task_id]);
return $task_id;
}
}
<?php
/**
* 智能文件分类器
*/
class Smart_File_Classifier {
private $adapter;
private $categories = [
'images' => ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'],
'documents' => ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt'],
'videos' => ['mp4', 'avi', 'mov', 'wmv', 'flv', 'mkv'],
'audio' => ['mp3', 'wav', 'ogg', 'm4a'],
'archives' => ['zip', 'rar', '7z', 'tar', 'gz'],
'code' => ['php', 'js', 'css', 'html', 'py', 'java', 'cpp']
];
public function __construct($adapter) {
$this->adapter = $adapter;
add_action('wp_ajax_classify_files', [$this, 'ajax_classify_files']);
add_action('add_attachment', [$this, 'auto_classify_new_file']);
}
public function classify_file($file_path, $file_name) {
$extension = strtolower(pathinfo($file_name, PATHINFO_EXTENSION));
// 根据扩展名分类
foreach ($this->categories as $category => $extensions) {
if (in_array($extension, $extensions)) {
return $category;
}
}
// 使用MIME类型进一步分类
$mime_type = $this->get_mime_type($file_path);
if ($mime_type) {
return $this->classify_by_mime_type($mime_type);
}
return 'other';
}
private function classify_by_mime_type($mime_type) {
$mime_categories = [
'image/' => 'images',
'application/pdf' => 'documents',
'application/msword' => 'documents',
'application/vnd.openxmlformats-officedocument' => 'documents',
'video/' => 'videos',
'audio/' => 'audio',
'application/zip' => 'archives',
'application/x-rar-compressed' => 'archives',
'text/' => 'documents'
];
foreach ($mime_categories as $prefix => $category) {
if (strpos($mime_type, $prefix) === 0) {
return $category;
}
}
return 'other';
}
public function auto_classify_new_file($attachment_id) {
$file_path = get_attached_file($attachment_id);
$file_name = basename($file_path);
$category = $this->classify_file($file_path, $file_name);
// 保存分类信息
update_post_meta($attachment_id, '_file_category', $category);
// 如果是图片,提取更多信息
if ($category === 'images') {
$this->extract_image_metadata($attachment_id, $file_path);
}
}
private function extract_image_metadata($attachment_id, $file_path) {
$metadata = [];
// 获取EXIF数据
if (function_exists('exif_read_data') && in_array(strtolower(pathinfo($file_path, PATHINFO_EXTENSION)), ['jpg', 'jpeg'])) {
$exif = @exif_read_data($file_path);
if ($exif) {
if (isset($exif['DateTimeOriginal'])) {
$metadata['taken_date'] = $exif['DateTimeOriginal'];
}
if (isset($exif['GPSLatitude']) && isset($exif['GPSLongitude'])) {
$metadata['gps'] = $this->convert_gps($exif['GPSLatitude'], $exif['GPSLongitude']);
}
if (isset($exif['Make'])) {
$metadata['camera_make'] = $exif['Make'];
}
if (isset($exif['Model'])) {
$metadata['camera_model'] = $exif['Model'];
}
}
}
// 获取图片尺寸
$image_size = getimagesize($file_path);
if ($image_size) {
$metadata['dimensions'] = [
'width' => $image_size[0],
'height' => $image_size[1]
];
$metadata['mime_type'] = $image_size['mime'];
}
// 计算文件哈希
$metadata['file_hash'] = md5_file($file_path);
// 保存元数据
update_post_meta($attachment_id, '_image_metadata', $metadata);
}
private function convert_gps($gps_lat, $gps_lon) {
// 将GPS坐标转换为十进制
$lat_degrees = count($gps_lat) > 0 ? $this->gps_to_degrees($gps_lat) : 0;
$lon_degrees = count($gps_lon) > 0 ? $this->gps_to_degrees($gps_lon) : 0;
// 确定半球
$lat_direction = ($gps_lat['GPSLatitudeRef'] == 'S') ? -1 : 1;
$lon_direction = ($gps_lon['GPSLongitudeRef'] == 'W') ? -1 : 1;
return [
'lat' => $lat_degrees * $lat_direction,
'lon' => $lon_degrees * $lon_direction
];
}
private function gps_to_degrees($gps_coordinate) {
$degrees = count($gps_coordinate) > 0 ? $this->gps_coordinate_to_number($gps_coordinate[0]) : 0;
$minutes = count($gps_coordinate) > 1 ? $this->gps_coordinate_to_number($gps_coordinate[1]) : 0;
$seconds = count($gps_coordinate) > 2 ? $this->gps_coordinate_to_number($gps_coordinate[2]) : 0;
return $degrees + ($minutes / 60) + ($seconds / 3600);
}
private function gps_coordinate_to_number($coordinate_part) {
$parts = explode('/', $coordinate_part);
if (count($parts) <= 0) {
return 0;
}
if (count($parts) == 1) {
return $parts[0];
}
return floatval($parts[0]) / floatval($parts[1]);
}
public function ajax_classify_files() {
check_ajax_referer('file_classifier', 'nonce');
$file_ids = array_map('intval', $_POST['file_ids']);
$results = [];
foreach ($file_ids as $file_id) {
$file = get_post($file_id);
if (!$file || $file->post_type != 'attachment') {
continue;
}
$file_path = get_attached_file($file_id);
$category = $this->classify_file($file_path, $file->post_title);
// 更新分类
update_post_meta($file_id, '_file_category', $category);
$results[] = [
'id' => $file_id,
'name' => $file->post_title,
'category' => $category,
'icon' => $this->get_category_icon($category)
];
}
wp_send_json_success([
'results' => $results,
'total' => count($results)
]);
}
private function get_category_icon($category) {
$icons = [
'images' => 'dashicons-format-image',
'documents' => 'dashicons-media-document',
'videos' => 'dashicons-format-video',
'audio' => 'dashicons-format-audio',
'archives' => 'dashicons-media-archive',
'code' => 'dashicons-editor-code',
'other' => 'dashicons-media-default'
];
return isset($icons[$category]) ? $icons[$category] : $icons['other'];
}
}
<?php
/**
* 智能文件分类器
*/
class Smart_File_Classifier {
private $adapter;
private $categories = [
'images' => ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'],
'documents' => ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt'],
'videos' => ['mp4', 'avi', 'mov', 'wmv', 'flv', 'mkv'],
'audio' => ['mp3', 'wav', 'ogg', 'm4a'],
'archives' => ['zip', 'rar', '7z', 'tar', 'gz'],
'code' => ['php', 'js', 'css', 'html', 'py', 'java', 'cpp']
];
public function __construct($adapter) {
$this->adapter = $adapter;
add_action('wp_ajax_classify_files', [$this, 'ajax_classify_files']);
add_action('add_attachment', [$this, 'auto_classify_new_file']);
}
public function classify_file($file_path, $file_name) {
$extension = strtolower(pathinfo($file_name, PATHINFO_EXTENSION));
// 根据扩展名分类
foreach ($this->categories as $category => $extensions) {
if (in_array($extension, $extensions)) {
return $category;
}
}
// 使用MIME类型进一步分类
$mime_type = $this->get_mime_type($file_path);
if ($mime_type) {
return $this->classify_by_mime_type($mime_type);
}
return 'other';
}
private function classify_by_mime_type($mime_type) {
$mime_categories = [
'image/' => 'images',
'application/pdf' => 'documents',
'application/msword' => 'documents',
'application/vnd.openxmlformats-officedocument' => 'documents',
'video/' => 'videos',
'audio/' => 'audio',
'application/zip' => 'archives',
'application/x-rar-compressed' => 'archives',
'text/' => 'documents'
];
foreach ($mime_categories as $prefix => $category) {
if (strpos($mime_type, $prefix) === 0) {
return $category;
}
}
return 'other';
}
public function auto_classify_new_file($attachment_id) {
$file_path = get_attached_file($attachment_id);
$file_name = basename($file_path);
$category = $this->classify_file($file_path, $file_name);
// 保存分类信息
update_post_meta($attachment_id, '_file_category', $category);
// 如果是图片,提取更多信息
if ($category === 'images') {
$this->extract_image_metadata($attachment_id, $file_path);
}
}
private function extract_image_metadata($attachment_id, $file_path) {
$metadata = [];
// 获取EXIF数据
if (function_exists('exif_read_data') && in_array(strtolower(pathinfo($file_path, PATHINFO_EXTENSION)), ['jpg', 'jpeg'])) {
$exif = @exif_read_data($file_path);
if ($exif) {
if (isset($exif['DateTimeOriginal'])) {
$metadata['taken_date'] = $exif['DateTimeOriginal'];
}
if (isset($exif['GPSLatitude']) && isset($exif['GPSLongitude'])) {
$metadata['gps'] = $this->convert_gps($exif['GPSLatitude'], $exif['GPSLongitude']);
}
if (isset($exif['Make'])) {
$metadata['camera_make'] = $exif['Make'];
}
if (isset($exif['Model'])) {
$metadata['camera_model'] = $exif['Model'];
}
}
}
// 获取图片尺寸
$image_size = getimagesize($file_path);
if ($image_size) {
$metadata['dimensions'] = [
'width' => $image_size[0],
'height' => $image_size[1]
];
$metadata['mime_type'] = $image_size['mime'];
}
// 计算文件哈希
$metadata['file_hash'] = md5_file($file_path);
// 保存元数据
update_post_meta($attachment_id, '_image_metadata', $metadata);
}
private function convert_gps($gps_lat, $gps_lon) {
// 将GPS坐标转换为十进制
$lat_degrees = count($gps_lat) > 0 ? $this->gps_to_degrees($gps_lat) : 0;
$lon_degrees = count($gps_lon) > 0 ? $this->gps_to_degrees($gps_lon) : 0;
// 确定半球
$lat_direction = ($gps_lat['GPSLatitudeRef'] == 'S') ? -1 : 1;
$lon_direction = ($gps_lon['GPSLongitudeRef'] == 'W') ? -1 : 1;
return [
'lat' => $lat_degrees * $lat_direction,
'lon' => $lon_degrees * $lon_direction
];
}
private function gps_to_degrees($gps_coordinate) {
$degrees = count($gps_coordinate) > 0 ? $this->gps_coordinate_to_number($gps_coordinate[0]) : 0;
$minutes = count($gps_coordinate) > 1 ? $this->gps_coordinate_to_number($gps_coordinate[1]) : 0;
$seconds = count($gps_coordinate) > 2 ? $this->gps_coordinate_to_number($gps_coordinate[2]) : 0;
return $degrees + ($minutes / 60) + ($seconds / 3600);
}
private function gps_coordinate_to_number($coordinate_part) {
$parts = explode('/', $coordinate_part);
if (count($parts) <= 0) {
return 0;
}
if (count($parts) == 1) {
return $parts[0];
}
return floatval($parts[0]) / floatval($parts[1]);
}
public function ajax_classify_files() {
check_ajax_referer('file_classifier', 'nonce');
$file_ids = array_map('intval', $_POST['file_ids']);
$results = [];
foreach ($file_ids as $file_id) {
$file = get_post($file_id);
if (!$file || $file->post_type != 'attachment') {
continue;
}
$file_path = get_attached_file($file_id);
$category = $this->classify_file($file_path, $file->post_title);
// 更新分类
update_post_meta($file_id, '_file_category', $category);
$results[] = [
'id' => $file_id,
'name' => $file->post_title,
'category' => $category,
'icon' => $this->get_category_icon($category)
];
}
wp_send_json_success([
'results' => $results,
'total' => count($results)
]);
}
private function get_category_icon($category) {
$icons = [
'images' => 'dashicons-format-image',
'documents' => 'dashicons-media-document',
'videos' => 'dashicons-format-video',
'audio' => 'dashicons-format-audio',
'archives' => 'dashicons-media-archive',
'code' => 'dashicons-editor-code',
'other' => 'dashicons-media-default'
];
return isset($icons[$category]) ? $icons[$category] : $icons['other'];
}
}
<?php
/**
* 文件分享与协作工具
*/
class File_Sharing_Tool {
private $adapter;
private $share_expiry_days = 7;
public function __construct($adapter) {
$this->adapter = $adapter;
add_action('wp_ajax_create_file_share', [$this, 'ajax_create_share']);
add_action('wp_ajax_revoke_file_share', [$this, 'ajax_revoke_share']);
add_action('wp', [$this, 'handle_shared_file_access']);
}
public function create_share_link($file_id, $options = []) {
global $wpdb;
// 验证文件存在且用户有权限
$file = $wpdb->get_row($wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}cloud_files WHERE id = %d",
$file_id
));
if (!$file) {
<?php
/**
* 文件分享与协作工具
*/
class File_Sharing_Tool {
private $adapter;
private $share_expiry_days = 7;
public function __construct($adapter) {
$this->adapter = $adapter;
add_action('wp_ajax_create_file_share', [$this, 'ajax_create_share']);
add_action('wp_ajax_revoke_file_share', [$this, 'ajax_revoke_share']);
add_action('wp', [$this, 'handle_shared_file_access']);
}
public function create_share_link($file_id, $options = []) {
global $wpdb;
// 验证文件存在且用户有权限
$file = $wpdb->get_row($wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}cloud_files WHERE id = %d",
$file_id
));
if (!$file) {


