PHP file_put_contents Blocco file

9

Il Senario:

Hai un file con una stringa (valore medio della frase) su ogni riga. Per motivi di argomenti, diciamo che questo file ha una dimensione di 1 Mb (migliaia di righe).

Hai uno script che legge il file, cambia alcune stringhe all'interno del documento (non solo aggiungendo ma anche rimuovendo e modificando alcune linee) e poi sovrascrive tutti i dati con i nuovi dati.

Le domande:

  1. Il "server" PHP, OS o httpd ecc. dispone già di sistemi per bloccare problemi come questo (lettura / scrittura a metà di una scrittura)?

  2. Se lo fa, spiega come funziona e fornisci esempi o collegamenti alla documentazione pertinente.

  3. In caso contrario, ci sono cose che posso abilitare o impostare, come il blocco di un file fino al completamento di una scrittura e il fallimento di tutte le altre letture e / o scritture fino al termine della scrittura precedente?

Le mie ipotesi e altre informazioni:

  1. Il server in questione esegue PHP e Apache o Lighttpd.

  2. Se lo script è chiamato da un utente e sta scrivendo a metà strada nel file e un altro utente legge il file in quel momento esatto. L'utente che lo legge non riceverà il documento completo, poiché non è stato ancora scritto. (Se questa ipotesi è sbagliata, correggimi)

  3. Mi interessano solo la scrittura e la lettura di PHP in un file di testo, e in particolare, le funzioni "fopen" / "fwrite" e principalmente "file_put_contents". Ho esaminato la documentazione "file_put_contents" ma non ho trovato il livello di dettaglio o una buona spiegazione di cosa sia o non sia il flag "LOCK_EX".

  4. Lo scenario è un esempio di uno scenario peggiore in cui presumerei che questi problemi siano più probabili, a causa delle grandi dimensioni del file e del modo in cui i dati vengono modificati. Voglio saperne di più su questi temi e non voglio o ho bisogno di risposte o commenti come "usa mysql" o "perché lo stai facendo" perché non lo sto facendo, voglio solo imparare a leggere / scrivere file con PHP e non sembra che stia cercando nelle giuste posizioni / documentazione e sì capisco che PHP non è la lingua perfetta per lavorare con i file in questo modo.

posta hozza 26.10.2012 - 16:36
fonte

4 risposte

4

1) No 3) No

Esistono diversi problemi con l'approccio suggerito inizialmente:

In primo luogo, alcuni sistemi simili a UNIX come Linux potrebbero non avere implementato il supporto per il blocco. Il sistema operativo non blocca i file per impostazione predefinita. Ho visto i syscalls essere NOP (nessuna operazione), ma è qualche anno fa, quindi è necessario verificare se un blocco impostato dall'istanza dell'applicazione è rispettato da un'altra istanza. (cioè 2 visitatori simultanei). Se il blocco non è ancora implementato [molto probabilmente lo è], il sistema operativo ti consente di sovrascrivere quel file.

La lettura di file di grandi dimensioni riga per riga non è fattibile per motivi di prestazioni. Suggerisco di usare file_get_contents () per caricare l'intero file in memoria e poi esplodere () per ottenere le linee. In alternativa, usa fread () per leggere il file in blocchi. L'obiettivo è ridurre al minimo il numero di chiamate in lettura.

Riguardo al blocco dei file:

LOCK_EX indica un blocco esclusivo (in genere per la scrittura). Solo un processo può contenere un blocco esclusivo per un dato file in un dato momento. LOCK_SH è un blocco condiviso (in genere per la lettura), più di un processo può contenere un blocco condiviso per un dato file in un dato momento. LOCK_UN sblocca il file. Lo sblocco viene eseguito automaticamente nel caso in cui tu usi file_get_contents () link

Soluzione elegante

PHP supporta i filtri del flusso di dati che sono destinati all'elaborazione di dati in file o da altri input. Potresti voler creare uno di questi filtri correttamente usando l'API standard. link link

Soluzione alternativa (in 3 passaggi):

  1. Crea una coda. Invece di elaborare un nome file, utilizzare il database o un altro meccanismo per archiviare nomi di file univoci da qualche parte in sospeso / ed elaborati in / elaborati. In questo modo niente viene sovrascritto. Il database sarà anche utile per memorizzare informazioni aggiuntive, come metadati, timestamp affidabili, risultati di elaborazione e altro.

  2. Per file di pochi MB, leggi l'intero file in memoria e poi elaboralo (file_get_contents () + explode () + foreach ())

  3. Per file più grandi, leggi il file in blocchi (es. 1024 Bytes) e procedi + scrivi in tempo reale ogni blocco come da lettura (attento all'ultima riga che non finisce con \ n. Deve essere elaborati nel batch successivo)

risposta data 01.12.2012 - 15:42
fonte
3

So che questo è antichissimo, ma nel caso qualcuno si imbattesse in questo. IMHO il modo per farlo è come questo:

1) Apri il file originale (ad esempio original.txt) utilizzando file_get_contents ('original.txt').

2) Apporta le modifiche / modifiche.

3) Usa file_put_contents ('original.txt.tmp') e scriverlo in un file temporaneo original.txt.tmp.

4) Quindi sposta il file tmp nel file originale, sostituendo il file originale. Per questo si usa rinominare ('original.txt.tmp', 'original.txt').

Vantaggi: Mentre il file viene elaborato e scritto nel file non è bloccato e altri possono ancora leggere il vecchio contenuto. Almeno sulle scatole Linux / Unix la rinomina è un'operazione atomica. Eventuali interruzioni durante la scrittura del file non toccano il file originale. Viene spostato solo una volta che il file è stato completamente scritto su disco. Leggi più interessanti su questo nei commenti a link

    
risposta data 01.10.2016 - 18:23
fonte
1

Nella documentazione PHP per file_put_contents () puoi trovare in example # 2 l'utilizzo di LOCK_EX , semplicemente:

file_put_contents('somefile.txt', 'some text', LOCK_EX);

Il LOCK_EX è una costante con un valore intero che può essere utilizzato su alcune funzioni in un bit a bit .

Ci sono anche una funzione specifica per controllare il blocco dei file: flock () modo .

    
risposta data 26.11.2012 - 16:22
fonte
0

Un problema che non hai menzionato è che devi stare attento anche alle condizioni di gara in cui due istanze del tuo script sono in esecuzione quasi allo stesso tempo, ad esempio questo ordine di occorrenze:

  1. Istanza script 1: legge il file
  2. Istanza di script 2: legge il file
  3. Istanza script 1: consente di scrivere le modifiche nel file
  4. Istanza script 2: sovrascrive le modifiche dell'istanza dello script prima al file con le proprie modifiche (poiché a questo punto la sua lettura è diventata obsoleta).

Quindi, quando si aggiorna un file di grandi dimensioni, è necessario LOCK_EX quel file prima di leggerlo e non rilasciare il blocco fino a quando non sono state effettuate le scritture. In questo esempio credo che farà sì che la seconda istanza di script si blocchi per un po 'mentre aspetta il suo turno per accedere al file, ma questo è meglio dei dati persi.

    
risposta data 16.04.2017 - 05:40
fonte

Leggi altre domande sui tag