You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Laravel 4.2实现本地与中央服务器数据及文件同步的简便方案问询

Hey there! Since you're working with Laravel 4.2 (I remember that version well—solid foundation even if it's a bit dated), there are some straightforward ways to build this sync feature. Let's break this down into manageable parts that fit your needs:

Core Requirements Recap
  • Trigger sync manually via a button when network is available
  • Sync both database records and user-uploaded files
  • Central server must identify which local instance the data comes from
Step-by-Step Implementation

1. Setup Source Identification First

First, you need a way for the central server to recognize your local app. Here's how:

  • Add a unique client_id (and optional client_secret for security) to your local app's app/config/app.php:
    'client_id' => 'local-app-001',
    'client_secret' => 'your-secure-secret-here',
    'central_server_url' => 'https://your-central-server.com'
    
  • On the central server, create a clients table to store valid client IDs/secrets (with columns like id, client_id, client_secret, name). This lets you validate incoming sync requests.

2. Database Data Sync

The goal here is to send unsynced records to the central server and mark them as synced locally once confirmed.

Local App Side:

  1. Add a synced_at timestamp column (nullable) to all tables you want to sync. Use a migration:
    Schema::table('your_table', function($table) {
        $table->timestamp('synced_at')->nullable();
    });
    
  2. Create a SyncController to handle the sync logic:
    <?php
    
    use Carbon\Carbon;
    use Illuminate\Support\Facades\Config;
    use Illuminate\Support\Facades\Input;
    use Illuminate\Support\Facades\Request;
    use Illuminate\Support\Facades\Response;
    
    class SyncController extends BaseController {
    
        public function syncData() {
            // First check if central server is reachable
            $centralUrl = Config::get('app.central_server_url');
            if (!$this->isCentralReachable($centralUrl)) {
                return Response::json(['status' => 'error', 'message' => 'Central server is offline'], 503);
            }
    
            // Fetch unsynced records (adjust the model to match yours)
            $unsyncedRecords = YourModel::whereNull('synced_at')->take(50)->get(); // Batch to avoid timeouts
            if ($unsyncedRecords->isEmpty()) {
                return Response::json(['status' => 'info', 'message' => 'No new data to sync']);
            }
    
            // Prepare data with client ID
            $payload = [
                'client_id' => Config::get('app.client_id'),
                'client_secret' => Config::get('app.client_secret'),
                'records' => $unsyncedRecords->toArray()
            ];
    
            // Send to central server
            $response = Request::post($centralUrl . '/api/sync/records', $payload);
            if ($response->status() === 200) {
                // Mark records as synced locally
                YourModel::whereIn('id', $unsyncedRecords->pluck('id'))->update(['synced_at' => Carbon::now()]);
                return Response::json(['status' => 'success', 'message' => count($unsyncedRecords) . ' records synced']);
            } else {
                return Response::json(['status' => 'error', 'message' => 'Failed to sync records'], $response->status());
            }
        }
    
        private function isCentralReachable($url) {
            // Simple connection check (adjust if you need more robustness)
            $headers = @get_headers($url);
            return $headers && strpos($headers[0], '200 OK') !== false;
        }
    }
    
  3. Add a route for this in app/routes.php:
    Route::post('/sync/data', 'SyncController@syncData');
    

Central Server Side:

Create an API endpoint to receive and store the synced data:

<?php

use Illuminate\Support\Facades\Input;
use Illuminate\Support\Facades\Response;

class SyncController extends BaseController {

   public function receiveRecords() {
       // Validate client credentials
       $clientId = Input::get('client_id');
       $clientSecret = Input::get('client_secret');
       $client = Client::where(['client_id' => $clientId, 'client_secret' => $clientSecret])->first();
       
       if (!$client) {
           return Response::json(['status' => 'error', 'message' => 'Unauthorized'], 403);
       }

       // Process incoming records
       $records = Input::get('records');
       foreach ($records as $record) {
           // Use updateOrCreate to avoid duplicates (map local ID to central record)
           CentralModel::updateOrCreate(
               ['local_id' => $record['id'], 'client_id' => $client->id],
               $record + ['client_id' => $client->id] // Attach client ID to the record
           );
       }

       return Response::json(['status' => 'success']);
   }
}

Add the route on the central server:

Route::post('/api/sync/records', 'SyncController@receiveRecords');

3. User Uploaded Files Sync

For files, use multipart uploads (more efficient for larger files):

Local App Side:

  1. Add a synced_at column to your uploaded_files table (similar to the database sync step).
  2. Add a method to your SyncController to handle file sync:
    public function syncFiles() {
        $centralUrl = Config::get('app.central_server_url');
        if (!$this->isCentralReachable($centralUrl)) {
            return Response::json(['status' => 'error', 'message' => 'Central server is offline'], 503);
        }
    
        $unsyncedFiles = UploadedFile::whereNull('synced_at')->take(10)->get();
        if ($unsyncedFiles->isEmpty()) {
            return Response::json(['status' => 'info', 'message' => 'No new files to sync']);
        }
    
        foreach ($unsyncedFiles as $file) {
            $filePath = public_path('uploads/' . $file->filename);
            if (!File::exists($filePath)) {
                continue; // Skip missing files
            }
    
            // Prepare multipart request
            $ch = curl_init($centralUrl . '/api/sync/file');
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, [
                'client_id' => Config::get('app.client_id'),
                'client_secret' => Config::get('app.client_secret'),
                'local_file_id' => $file->id,
                'file' => new CURLFile($filePath),
                'original_name' => $file->original_name
            ]);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            $response = json_decode(curl_exec($ch), true);
            curl_close($ch);
    
            if ($response['status'] === 'success') {
                UploadedFile::where('id', $file->id)->update(['synced_at' => Carbon::now()]);
            }
        }
    
        return Response::json(['status' => 'success', 'message' => 'File sync completed']);
    }
    
  3. Add the route:
    Route::post('/sync/files', 'SyncController@syncFiles');
    

Central Server Side:

Create the file receive endpoint:

public function receiveFile() {
   // Validate client first
   $clientId = Input::get('client_id');
   $clientSecret = Input::get('client_secret');
   $client = Client::where(['client_id' => $clientId, 'client_secret' => $clientSecret])->first();
   
   if (!$client) {
       return Response::json(['status' => 'error', 'message' => 'Unauthorized'], 403);
   }

   // Handle file upload
   $file = Input::file('file');
   $localFileId = Input::get('local_file_id');
   $originalName = Input::get('original_name');

   // Store file with client prefix to avoid conflicts
   $storagePath = 'uploads/' . $client->client_id . '_' . time() . '_' . $file->getClientOriginalName();
   $file->move(storage_path('uploads'), basename($storagePath));

   // Record file metadata in central database
   CentralUploadedFile::create([
       'client_id' => $client->id,
       'local_file_id' => $localFileId,
       'original_name' => $originalName,
       'file_path' => $storagePath
   ]);

   return Response::json(['status' => 'success']);
}

Add the route:

Route::post('/api/sync/file', 'SyncController@receiveFile');

4. Trigger Sync via Button

Add a frontend button to trigger both syncs (you can combine them into one controller method if preferred):

<button id="sync-all-btn" class="btn btn-primary">Sync to Central Server</button>

<script src="//code.jquery.com/jquery-1.12.4.min.js"></script>
<script>
$('#sync-all-btn').click(function() {
   const $btn = $(this);
   $btn.text('Syncing...').prop('disabled', true);

   // First sync data, then files
   $.ajax({
       url: '/sync/data',
       method: 'POST',
       headers: { 'X-CSRF-Token': '{{ csrf_token() }}' },
       success: function(data) {
           console.log(data.message);
           // Now sync files
           $.ajax({
               url: '/sync/files',
               method: 'POST',
               headers: { 'X-CSRF-Token': '{{ csrf_token() }}' },
               success: function(fileData) {
                   alert(data.message + '\n' + fileData.message);
                   $btn.text('Sync to Central Server').prop('disabled', false);
               },
               error: function(xhr) {
                   alert('File sync failed: ' + xhr.responseJSON.message);
                   $btn.text('Sync to Central Server').prop('disabled', false);
               }
           });
       },
       error: function(xhr) {
           alert('Data sync failed: ' + xhr.responseJSON.message);
           $btn.text('Sync to Central Server').prop('disabled', false);
       }
   });
});
</script>

5. Bonus: Error Handling & Retries

To make this more robust:

  • Add a sync_attempts column to your local tables (default 0). Increment it each time a sync fails.
  • Stop retrying after 3-5 attempts and mark the record/file as sync_failed so you can review it later.
  • Log failed syncs to Laravel's log system with Log::error('Sync failed for record ID: ' . $record->id);

6. Scaling with Queues (Optional)

If you have large amounts of data/files, use Laravel 4.2's queue system to offload sync tasks to the background:

  1. Set up a queue driver (like database) in app/config/queue.php.
  2. Create a queue worker class:
    class SyncWorker {
        public function syncRecords($job, $data) {
            // Reuse the sync logic from SyncController here
            $job->delete(); // Delete job on success
        }
    
        public function syncFiles($job, $data) {
            // Reuse file sync logic here
            $job->delete();
        }
    }
    
  3. Push jobs to the queue instead of executing immediately:
    Queue::push('SyncWorker@syncRecords', ['records' => $unsyncedRecords->toArray()]);
    
  4. Run the queue worker with php artisan queue:listen.
Final Notes
  • Since Laravel 4.2 is end-of-life, make sure your server is secure and you're using the latest patches available for it.
  • For extra security, consider using HTTPS for all sync requests (which you should be doing anyway).
  • Test the sync flow thoroughly with edge cases (network drops mid-sync, duplicate records, large files).

内容的提问来源于stack exchange,提问作者szskdgi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 06:45:09