Je télécharge un fichier CSV d'un autre serveur en tant que flux de données d'un fournisseur. P>
J'utilise CURL pour obtenir le contenu du fichier et enregistrer cela dans une variable appelée Je peux arriver à cette partie juste bien, mais j'ai essayé d'exploser par i J'ai besoin de manipuler les valeurs et de les insérer dans une base de données. Que dois-je faire pour éviter les erreurs d'allocation de mémoire? P> $ contenu code>. p>
\ r code> et
\ n code> pour obtenir une gamme de lignes mais elle échoue avec un "hors de Mémoire 'Erreur. P>
Echo Strlen ($ contenus) code> et il s'agit d'environ 30,5 millions de caractères. P>
7 Réponses :
spool à un fichier. N'essayez pas de conserver toutes ces données en mémoire à la fois. P>
memory_limit code> dans php.ini code>. li>
- lire des données en utilisant
fopen () code> et fgets () code>. li>
ol>
Vous voudrez peut-être envisager de l'enregistrer dans un fichier temporaire, puis la lire une ligne à la fois en utilisant De cette façon, vous évitez le premier tableau de grande taille que vous obtenez d'exploser une si grande chaîne. P> fgets code>
ou fgetcsv code> . p>
php étouffe parce que cela manque de mémoire. Au lieu d'avoir Curl peupler une variable PHP avec le contenu du fichier, utilisez l'option
//pseudo, untested code to give you the idea $fp = fopen('path/to/save/file', 'w'); curl_setopt($ch, CURLOPT_FILE, $fp); curl_exec ($ch); curl_close ($ch); fclose($fp);
La réponse à Stackoverflow.com/a/1342760/4668 est une meilleure que la mienne.
comme d'autres réponses indiquées:
curlopt_file code> li>
ul> Mais, vous ne voudrez peut-être pas vraiment créer un fichier que vous pouvez utiliser avec des données en mémoire ... en l'utilisant dès qu'il "arrive". p>
une solution possible Peut-être définir votre propre wrapper de flux et utiliser celui-ci, au lieu d'un fichier réel, avec curlopt_file code> p> Tout d'abord, voir: P>
-
stream_wrapper_register code>
< / li>
-
La classe StreamWrapper code>
li >
- Exemple de classe enregistrée comme wrapper de flux li >
ul>
Et maintenant, allons avec un exemple. P>
Tout d'abord, créez notre classe wrapper de flux: p> xxx pré> Ce que je fais est: p> < ul>
- Travailler sur les morceaux de données (j'utilise Var_Dummp, mais que vous feriez vos affaires habituelles à la place) quand elles arrivent li>
- Notez que vous n'obtenez pas de "lignes complètes": la fin d'une ligne est le début d'un morceau, et le début de la même ligne était à la fin du chunk précédent, vous devez donc garder certaines parties d'un morceau entre les appels vers
stream_write code> li>
ul>
Ensuite, nous enregistrons ce wrapper de flux, à utiliser avec le pseudo-protocole "test": p> xxx pré>
Et, maintenant, nous faisons notre demande de courbure, comme nous le ferions lors de l'écriture d'un fichier "réel", comme d'autres réponses suggérées: p> xxx pré> Notez que nous ne travaillons pas avec un vrai fichier, mais avec notre pseudo-protocole. P>
Ainsi, chaque fois qu'un morceau de données arrive, mystream :: stream_write code> sera appelé, et pourra travailler sur une petite quantité de données (quand j'ai testé, j'ai toujours été 8192 octets, quelle que soit la valeur que j'ai utilisée pour curlopt_buffersize code>) em> p>
Quelques notes: p>
- Vous devez tester cela plus que ce que j'ai fait, évidemment li>
- My Stream_Write La mise en œuvre ne fonctionnera probablement pas si les lignes sont plus longues de 8192 octets à vous de le corriger; -) li>
- Cela signifiait que quelques pointeurs, et non une solution entièrement de travail: vous devez tester (encore), et probablement coder un peu plus! Li>
ul>
Néanmoins, j'espère que cela aide ;-)
amusez-vous! p> p>
+1 pour cela! J'aime juste ajouter que lorsque vous traitez avec des données binaires, vous souhaitez générer directement les données de $ et ne pas la toucher du tout puisque cela le corrompre probablement.
Astucieux. Puisque curl 7.9.7 curlopt_file code> a été renommé
curlopt_wredata code>, et je pense que vous pouvez maintenant faire quelque chose de similaire en utilisant
curlopt_writtfunction code>, qui est un rappel comme votre
stream_write ($ données) code> et enregistre le besoin de l'enveloppe de flux. Voir curl.haxx.se/libcurl/c/curl_easy_setopt.html
NB:
"Fondamentalement, si vous ouvrez un fichier avec fopen, fcerez-le, puis dissimulez-le, ça fonctionne bien. Mais si entre fopen et fclose, vous donnez la poignée du fichier Pour courbonner pour faire de l'écriture dans le fichier, le dissimulez-vous échoue. Pourquoi Cela se passe est au-delà de moi. Je pense que cela peut être lié au bogue # 48676 " p>
http: //bugs.php.net/bug.php?id=49517 p>
Soyez donc prudent si vous êtes sur une ancienne version de php. Il y a une solution simple sur cette page à doubler -Close la ressource de fichier: p>
Darren Cook Commentaire à Pascal Martin La réponse est vraiment intéressante. Dans les versions modernes PHP + CURL, l'option Callables peut être des méthodes dans PHP. En utilisant tout cela, j'ai développé une solution possible que je trouve plus lisible que le précédent (bien que Pascal Martin, la réponse soit vraiment excellente, les choses ont changé depuis). J'ai utilisé des attributs publics pour la simplicité, mais je suis sûr que les lecteurs pourraient adapter et améliorer le code. Vous pouvez même avorter la demande de courbure lorsque plusieurs lignes (ou octets) ont été atteintes. J'espère que cela serait utile pour les autres. P> CURLOPT_WRITEFunction CODE> peut être définie afin que CURL invoque un rappel pour chaque "morceau" reçu de données. Plus précisément, le "appelable" recevra deux paramètres, le premier avec l'objet d'invocation de CURL, et le second avec le morceau de données. Le fonctionnement doit revenir
strallen ($ data) code> pour que CURL puis continue à envoyer plus de données.
<?
class SplitCurlByLines {
public function curlCallback($curl, $data) {
$this->currentLine .= $data;
$lines = explode("\n", $this->currentLine);
// The last line could be unfinished. We should not
// proccess it yet.
$numLines = count($lines) - 1;
$this->currentLine = $lines[$numLines]; // Save for the next callback.
for ($i = 0; $i < $numLines; ++$i) {
$this->processLine($lines[$i]); // Do whatever you want
++$this->totalLineCount; // Statistics.
$this->totalLength += strlen($lines[$i]) + 1;
}
return strlen($data); // Ask curl for more data (!= value will stop).
}
public function processLine($str) {
// Do what ever you want (split CSV, ...).
echo $str . "\n";
}
public $currentLine = '';
public $totalLineCount = 0;
public $totalLength = 0;
} // SplitCurlByLines
// Just for testing, I will echo the content of Stackoverflow
// main page. To avoid artifacts, I will inform the browser about
// plain text MIME type, so the source code should be vissible.
Header('Content-type: text/plain');
$splitter = new SplitCurlByLines();
// Configuration of curl
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "http://stackoverflow.com/");
curl_setopt($ch, CURLOPT_WRITEFUNCTION, array($splitter, 'curlCallback'));
curl_exec($ch);
// Process the last line.
$splitter->processLine($splitter->currentLine);
curl_close($ch);
error_log($splitter->totalLineCount . " lines; " .
$splitter->totalLength . " bytes.");
?>