PHP Upload Script Probleme.

smallwall

Lt. Junior Grade
Registriert
Feb. 2014
Beiträge
446
Hallo,
ich habe ein neues Problem mit meinem Upload Script. Es wandelt Umlaute in ? oder Kästchen um. Ich lade von einem Windows 10 Rechner hoch, auf dem Server kommt "murks" an, öäüß sind Kästchen oder "? ". Habe ein eigenes Script geschrieben, mir ein paar Scripts heruntergeladen und benutzt, immer der gleiche Fehler. Ich habe das mit .pgn Dateien probiert, diese sind keine Binärdateien, sie sind ähnlich .txt Dateien.

Ich poste mal meinen Code, aber mit fremden Code kommt ebenfalls das gleiche auf dem Server an, die Umlaute machen die gleichen Probleme.

$uploaddir = './pgnfiles/';
$uploadfile = $uploaddir . basename($_FILES['userfile']['name']);

if (file_exists($uploadfile)) {
die("Die Datei $uploadfile existiert bereits.");
} else {

$allowedExts = array("pgn");
$temp = explode(".", $_FILES["userfile"]["name"]);
$extension = end($temp);

if (($_FILES["userfile"]["size"] < 200000000) &&
in_array($extension, $allowedExts) && ($_FILES["userfile"]["error"] == 0)) {
if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) {
echo "Datei ist valide und wurde erfolgreich hochgeladen.\n";
} else {
echo "Dateiupload fehlgeschlagen!\n";
echo $uploadfile;
}
} else {
echo "Dateiupload fehlgeschlagen!\n";
echo("Zulässige Dateigröße überschritten?");
}
}

Ein Beispiel hier: http://www.devphp.de/pgn/showpgn.php?id=15 CLTR+F "schr" zeigt Schr�der statt Schröder.
Was mache ich falsch? Wo ist der Haken?

Bin für jede Hilfe oder Tipps dankbar,

mfg
 
Ist wohl einer falschen Kodierung geschuldet.
Du solltest für Westeuropa UTF-8 oder ISO 8859-1 verwenden.
 
Wie genau mache ich das? Muss ich utf8_encode() auf die hochgeladen Datei anwenden?

Edit: ja, das scheint die Lösung zu sein. Manchmal ist es so simpel, aber man kommt nicht alleine drauf =)
 
Zuletzt bearbeitet:
Wieso nicht? mb_convert_encoding($str, "UTF-8") scheint mir das selbe zu sein. Bei iconv muss man zusätzlich noch die Quell-Kodierung kennen. Da die Datei "überall" her kommen kann, scheint mir das nicht so gut geeignet zu sein.
 
Deshalb:
Code:
<?php

$s = "ÄÖÜßäöü?$%&/€@µ";
$se1 = utf8_encode( $s );
$se2 = utf8_encode( $se1 );

var_dump( [
	"plain" => $s,
	"utf8_encode 1x" => $se1,
	"utf8_encode 2x" => $se2
] );

$sm1 = mb_convert_encoding( $s, "UTF-8", mb_detect_encoding( $s ) );
$sm2 = mb_convert_encoding( $s, "UTF-8", mb_detect_encoding( $sm1 ) );

var_dump( [
	"plain" => $s,
	"mb_convert_encoding 1x" => $sm1,
	"mb_convert_encoding 2x" => $sm2
] );
Code:
array (size=3)
  'plain' => string 'ÄÖÜßäöü?$%&/€@µ' (length=25)
  'utf8_encode 1x' => string 'ÄÖÜßäöü?$%&/€@µ' (length=44)
  'utf8_encode 2x' => string 'ÄÖÜßäöü?$%&/€@µ' (length=82)

array (size=3)
  'plain' => string 'ÄÖÜßäöü?$%&/€@µ' (length=25)
  'mb_convert_encoding 1x' => string 'ÄÖÜßäöü?$%&/€@µ' (length=25)
  'mb_convert_encoding 2x' => string 'ÄÖÜßäöü?$%&/€@µ' (length=25)
 
PHP:
$sm1 = mb_convert_encoding( $s, "UTF-8", mb_detect_encoding( $s ) );
... you say? Sounds brilliant! :)
 
Hi, ich habe mein UploadScript geändert. Seit dem gibt er mir folgende Fehlermeldung bei einigen Files.
Code:
Warning: mb_convert_encoding(): Illegal character encoding specified in /home/devphpde/public_html/pgn/upload_file3.php on line 273
Das ist Zeile 49 im Quote, den Rest habe ich mal weggelassen. Im alten Script hat die Zeile keine Probleme bereitet.

Weiter macht das Script Probleme, wenn mehrmals die gleiche Datei hochgeladen werden soll. Beim ersten upload funktioniert es, beim 2. mal diese 2 Meldungen
Code:
Warning: move_uploaded_file(/newpgnfiles/McShane_8unm0v.pgn): failed to open stream: No such file or directory in /home/devphpde/public_html/pgn/upload_file3.php on line 262

Warning: move_uploaded_file(): Unable to move '/tmp/phpm65LxF' to '/newpgnfiles/McShane_8unm0v.pgn' in /home/devphpde/public_html/pgn/upload_file3.php on line 262
Dateiupload fehlgeschlagen! /newpgnfiles/McShane_8unm0v.pgn
Das ist Zeile 46. Wenn der Dateiname der Originale ist, gibt es keine Probleme, kann das nicht funktionieren wie ich mir das gedacht habe? Kann man nur den Originalnamen benutzen? In diesem Fall könnte ich die Datei später wieder löschen/verschieben um eine ähnliche Funktionalität zu haben.

PHP:
<?php

// search for UMLAUTE etc, replace in filename:

$allowedExts = array("pgn", "PGN", "Pgn", "pGn", "pgN", "PGn", "PgN", "pGN");
$temp = explode(".", $_FILES["userfile"]["name"]);
$extension = end($temp);
$maxSize = 2000000;
$uploaddir = './newpgnfiles/';
if (!file_exists($uploaddir)) {
    mkdir($uploaddir, 0777, true);
}
$search = array('ä', 'ö', 'ü', 'ß', 'Ä', 'Ö', 'Ü', ' ');
$replace = array('ae', 'oe', 'ue', 'ss', 'Ae', 'Oe', 'Ue', '_');

$filename2 = basename($_FILES['userfile']['name']);
$filename = str_replace($search, $replace, $filename2);

$uploadfile = $uploaddir . $filename;

function findNewFileName($filen) {

    $temp = explode(".", $filen);
    $extension = end($temp);
    $sliced = array_slice($temp, 0, -1);
    $imploded = implode($sliced);
    $addrandom = '';
    $characters = 'abcdefghijklmnopqrstuvwxyz0123456789';
    for ($i = 0; $i < 6; $i++) {
        $addrandom .= $characters[rand(0, strlen($characters) - 1)];
    }
    $newfilename = $imploded . "_" . $addrandom . "." . $extension;

    return $newfilename;
}

if (file_exists($uploadfile)) {

    // find new filename
    $uploadfile = findNewFileName($uploadfile);

    //die("Die Datei $uploadfile existiert bereits.");
}

if (($_FILES["userfile"]["size"] < $maxSize) && in_array($extension, $allowedExts) && ($_FILES["userfile"]["error"] == 0)) {
    if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) {
        // get content, encode it to UTF8, put content back into file
        $file_contents = file_get_contents($uploadfile);
        $encoded_content = mb_convert_encoding( $file_contents, "UTF-8", mb_detect_encoding( $file_contents ) );
        file_put_contents($uploadfile, $encoded_content);
        echo("Ihre Datei wurde erfolgreich übertragen.<br>\n");
        echo 'Alles unterhalb dieser Zeile sollte für Sie irrelevant sein<br>';
        echo'<a href="javascript:history.back()">Zurück</a><br><br>' . PHP_EOL;
    } else {
        echo "Dateiupload fehlgeschlagen!\n";
        echo $uploadfile;
    }
} else {
    echo "Dateiupload fehlgeschlagen!\n";
    echo("Zulässige Dateigröße überschritten?");
}
 
mb_detect_encoding 'rät' was die Datei für ein Encoding hat, klappt das nicht gibt es false zurück. Wenn du dann false als Quellencoding nach mb_convert_encoding gibst, meckert das natürlich (völlig zu Recht) rum.

Ich würde if(mb_detect_encoding) {...} machen und vielleicht noch ein else mit einem Fallback. Wenn das Quellencoding nicht klar ist Windows 1252 annehmen oder so, je nachdem wer da hochlädt und womit.

Weiter macht das Script Probleme, wenn mehrmals die gleiche Datei hochgeladen werden soll. Beim ersten upload funktioniert es, beim 2. mal diese 2 Meldungen

Lädst du mehrer Dateien _gleichzeitig_ hoch oder mehrere hintereinander, sprich wie oft klickst du bei 2 Dateien auf Upload?
 
Im Moment wird immer nur eine Datei hochgeladen. Ich drücke "Back" im Browser und drücke nochmal upload.
Deine Idee mit dem if mb_detect_encoding ist gut, das bringt mich schonmal weiter. Aber wieso heißt es detect und nicht try_to_detect oder guess? Naja wenn es nicht eindeutig zu ermitteln ist... So ausgefallen kann die Datei aber nicht sein, ich schau mir mal an was dort detected wird, bzw nicht...

Naja wenn es false ist... gibt es noch andere Möglichkeiten das Encoding herauszufinden? Ich kenne es ja auch nicht, und wenn die Funktion schon Probleme damit hat...
 
Zuletzt bearbeitet:
Mach mal ein print_r(mb_detect_order());, das sollte die Encodings zurückgeben. Ich könnte dir jetzt erklären wie das genau funktioniert, aber das macht das Manual schon ganz gut:
http://php.net/manual/de/function.mb-detect-order.php
http://php.net/manual/de/function.mb-detect-encoding.php

Im Wesentlichen gibts ein zentrales Array, was die möglichen Quellencodings hält und mit mb_detect_order abgerufen/gesetzt werden kann. mb_detect_encoding benutzt das dann, oder optional kann man auch eine eigene Liste möglicher Quellencodings mitgeben. Ich denke mal das kommt drauf an, wie dein Hoster bzw deine Distro das standardmäßig einstellt, wenn da zB Windows 1252 als Encoding in der Datei steht, wird das IMHO nicht abgehandelt mit den Standardquellencodings (aber wie gesagt, ich glaube das ist Einstellungssache, deswegen das print_r oben).

Ich glaub dein Uploadproblem kann ich auch auflösen:

PHP:
$uploaddir = './newpgnfiles/';
$uploadfile = $uploaddir . $filename;
if (file_exists($uploadfile)) {
     $uploadfile = findNewFileName($uploadfile);
 }

function findNewFileName($filen) {
     $temp = explode(".", $filen);
[...]
}

Dein Uploaddir ist am Anfang './newpgnfiles/', durch das implode fällt da aber vorne der Punkt weg, das heißt er sucht dann im Rootverzeichnis nach dem Ordner, den gibts nicht, ergo kapputt.

Da gibts allgemein ein paar Ecken in dem Script die ich etwas kompliziert gelöst finde. Die Geschichte mit den erlaubten Extensions zB, was spricht dagegen ein strtolower auf den Dateinamen zu machen und dann auf pgn zu testen? Respektive wenn du eh nur pgn Files erlaubst, dann guck doch nach dem Upload gleich ob es wirklich ein pgn File ist, indem dus mal durchschaust, eine Funktion dafür wirst du ja schon haben? Oder dann eben die Geschicht mit dem neuen Dateinamen, da hätte ich einfach
PHP:
function findNewFileName($filen) {
  $zaehl = 1;  
  while(is_file(str_replace('.pgn', $zaehl.'.pgn', $filen)) && $zaehl < 100) {
    $zaehl++;
  }
  if(!is_file(str_replace('.pgn', $zaehl.'.pgn', $filen))) {
   $filen = str_replace('.pgn', $zaehl.'.pgn', $filen);
   return($filen)
 } else {
  die('konnte keinen Dateinamen ermitteln');
 }
}

gemacht. Also vorrausgesetzt der übergebene $filen ist vorher mit strtolower() behandelt. Oder halt einfach mit uniqueid() einen hash generiert und den reingeschrieben, kann man halten wie ein Dachdecker.

So, viel Erfolg damit :D
 
Danke, du hast den fehlenden Punkt gefunden. Die Methode mit strtolower um die Extension zu prüfen ist wesentlich besser als die mit in_array(); Habe es jetzt geändert, ich quote nochmal unten.

Bleibt das Problem mit der encoding detection. print_r(mb_detect_order()); zeigt bei mir
Array ( [0] => ASCII [1] => UTF-8 )
Heißt das, nur ASCII und UTF-8 kann detected werden? Muss ich dort alle Encodings angeben, damit sie erkannt werden können? Das wäre fatal, müsste eine Liste googlen.

Deine findNewFileName habe ich versucht zu benutzen, aber irgendwie lief sie nicht richtig, aber die Idee mit den Zahlen ist auch gut. Ich wollte führende nullen mit str_pad($zaehl, 4 ,'0', STR_PAD_LEFT); dazu schreiben, aber bei meinen tests hat er einfach immer 0001 angehangen, also 00010001, etc.. Habe dann mein alte weiter verwendet.

Vor allem habe ich den Funktionsaufruf geändert, von if auf while, dann sucht er halt so lange einen neuen Namen bis die Datei noch nicht vorhanden ist.

Hier der Quote des updates.
PHP:
removed

Edit:Ups, da stimmt was nicht, da ist zu viel wiederholung...
Edit2: so müsste es passen
Ergänzung ()

Joa, das scheint zu klappen mit dem setzen der Encoding list, hier für googler mal der Quote
PHP:
$enclist = "UCS-4,UCS-4BE,UCS-4LE,UCS-2,UCS-2BE,UCS-2LE,UTF-32,UTF-32BE,UTF-32LE,UTF-16,";
$enclist .= "UTF-16BE,UTF-16LE,UTF-7,UTF7-IMAP,UTF-8,ASCII,EUC-JP,SJIS,eucJP-win,SJIS-win";
$enclist .= "ISO-2022-JP,ISO-2022-JP-MS,CP932,CP51932,SJIS-mac,SJIS-Mobile#DOCOMO";
$enclist .= "SJIS-Mobile#KDDI,SJIS-Mobile#SOFTBANK,UTF-8-Mobile#DOCOMO,UTF-8-Mobile#KDDI-A";
$enclist .= "UTF-8-Mobile#KDDI-B,UTF-8-Mobile#SOFTBANK,ISO-2022-JP-MOBILE#KDDI";
$enclist .= "JIS,JIS-ms,CP50220,CP50220raw,CP50221,CP50222,ISO-8859-1,ISO-8859-2";
$enclist .= "ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,";
$enclist .= "ISO-8859-10,ISO-8859-13,ISO-8859-14,ISO-8859-15,byte2be,byte2le,";
$enclist .= "byte4be,byte4le,BASE64,HTML-ENTITIES,7bit,8bit,EUC-CN,CP936,GB18030,HZ,EUC-TW,";
$enclist .= "CP950,BIG-5,EUC-KR,UHC (CP949),ISO-2022-KR,Windows-1251 (CP1251),";
$enclist .= "Windows-1252 (CP1252),CP866 (IBM866),KOI8-R,ArmSCII-8 (ArmSCII8)";

mb_detect_order($enclist);

Damit wurden auch Dateien erkannt, die vorher nicht erkannt wurden. Hier der komplette Quote meines Uploadscripts
PHP:
if (isset($_FILES) && !empty($_FILES)) {

    $temp = explode(".", $_FILES["userfile"]["name"]);

    $extension = end($temp);
    $orgext = $extension;

    if (strtolower($extension) == "pgn") {
        $extension = $orgext;
    } else {
        die("Invalid Extension");
    }

    $maxSize = 2000000;

    $uploaddir = './newpgnfiles/';

    if (!file_exists($uploaddir)) {
        mkdir($uploaddir, 0777, true);
    }

    $search = array('ä', 'ö', 'ü', 'ß', 'Ä', 'Ö', 'Ü', ' ');
    $replace = array('ae', 'oe', 'ue', 'ss', 'Ae', 'Oe', 'Ue', '_');

    $filename2 = basename($_FILES['userfile']['name']);

    $filename = str_replace($search, $replace, $filename2);

    $uploadfile = $uploaddir . $filename;

    function findNewFileName($filen) {

        $temp = explode(".", $filen);
        $extension = end($temp);
        $sliced = array_slice($temp, 0, -1);

        $imploded = implode($sliced);
        $addrandom = '';
        $characters = 'abcdefghijklmnopqrstuvwxyz0123456789';
        for ($i = 0; $i < 6; $i++) {
            $addrandom .= $characters[rand(0, strlen($characters) - 1)];
        }
        $newfilename = "." . $imploded . "_renamed_" . $addrandom . "." . $extension;

        return $newfilename;
    }

    function is_pgn($text) {

        $hasEvent = (strpos($text, "[Event ") !== false) ? true : false;
        $hasSite = (strpos($text, "[Site ") !== false) ? true : false;
        $hasWhite = (strpos($text, "[White ") !== false) ? true : false;
        $hasBlack = (strpos($text, "[Black ") !== false) ? true : false;
        $hasResult = (strpos($text, "[Result ") !== false) ? true : false;

        if ($hasEvent && $hasBlack && $hasSite && $hasWhite && $hasResult) {
            return true;
        } else {
            die("Not valid .pgn");
        }
    }

    while (file_exists($uploadfile)) {
        $uploadfile = findNewFileName($uploadfile);
    }

    if (($_FILES["userfile"]["size"] < $maxSize) && ($_FILES["userfile"]["error"] == 0)) {
        if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) {
            // get content, encode it to UTF8, put content back into file
            $file_contents = file_get_contents($uploadfile);

            $enclist = "UCS-4,UCS-4BE,UCS-4LE,UCS-2,UCS-2BE,UCS-2LE,UTF-32,UTF-32BE,UTF-32LE,UTF-16,";
            $enclist .= "UTF-16BE,UTF-16LE,UTF-7,UTF7-IMAP,UTF-8,ASCII,EUC-JP,SJIS,eucJP-win,SJIS-win";
            $enclist .= "ISO-2022-JP,ISO-2022-JP-MS,CP932,CP51932,SJIS-mac,SJIS-Mobile#DOCOMO";
            $enclist .= "SJIS-Mobile#KDDI,SJIS-Mobile#SOFTBANK,UTF-8-Mobile#DOCOMO,UTF-8-Mobile#KDDI-A";
            $enclist .= "UTF-8-Mobile#KDDI-B,UTF-8-Mobile#SOFTBANK,ISO-2022-JP-MOBILE#KDDI";
            $enclist .= "JIS,JIS-ms,CP50220,CP50220raw,CP50221,CP50222,ISO-8859-1,ISO-8859-2";
            $enclist .= "ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,";
            $enclist .= "ISO-8859-10,ISO-8859-13,ISO-8859-14,ISO-8859-15,byte2be,byte2le,";
            $enclist .= "byte4be,byte4le,BASE64,HTML-ENTITIES,7bit,8bit,EUC-CN,CP936,GB18030,HZ,EUC-TW,";
            $enclist .= "CP950,BIG-5,EUC-KR,UHC (CP949),ISO-2022-KR,Windows-1251 (CP1251),";
            $enclist .= "Windows-1252 (CP1252),CP866 (IBM866),KOI8-R,ArmSCII-8 (ArmSCII8)";

            mb_detect_order($enclist);

            if (mb_detect_encoding($file_contents) != false) {

                $encoded_content = mb_convert_encoding($file_contents, "UTF-8", mb_detect_encoding($file_contents));

                if (is_pgn($encoded_content)) {
                    file_put_contents($uploadfile, $encoded_content);
                    echo("Ihre Datei wurde erfolgreich übertragen <br>" . PHP_EOL);
                    echo '<a href="javascript:history.back()">Zurück</a><br>' . PHP_EOL;
                    echo 'Alles unterhalb dieser Zeile sollte für Sie irrelevant sein<br>' . PHP_EOL;
                    write2db($uploadfile, $pw, $mail, $name);
                }
            } else {
                echo "<br>mb_detect_encoding() IS FALSE<br>" . PHP_EOL;
            }
        } else {
            echo "Dateiupload fehlgeschlagen!\n";
            echo $uploadfile;
        }
    } else {
        echo "Dateiupload fehlgeschlagen!\n";
        echo("Zulässige Dateigröße überschritten?");
    }
}
Ergänzung ()

Und da ist gleich das nächste Problem: Nachdem die Datei hochgeladen wurde, wird sie gelesen und ein Sqlqry erstellt, das klappt aber nicht mit zusätlichen Zeichen, die in der Datei stehen.
Code:
INSERT INTO TEST_pgn_TESTBASE (��[Event, Site,
Das gehört eher in den anderen Thread, aber egal. Die 2 ? und die [ sollten dort nicht stehen. Wie werde ich die Zeichen los? Die Datei wurde doch in UTF-8 re-codiert? Wieso sind die Zeichen einfach trotzdem unbrauchbar?

Edit: mit is_pgn() schaue ich jetzt, ob einige übliche Header vorhanden sind, ich denke das reicht. Wenn es invalid ist, könnte man die Datei auch wieder löschen/verschieben, da sie ja nicht brauchbar ist für den Rest des Skripts.
 
Zuletzt bearbeitet: (is_pgn added)
Ich glaub deine Encodingliste ist etwas zu heftig, in westlichen Gefilden würde ich
'UTF-8,ASCII,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-13,ISO-8859-14,ISO-8859-15,ISO-8859-16,Windows-1251,Windows-1252,Windows-1254'

nehmen, Irgendwelche kyrillischen oder japanischen Encodings kannst du dir da glaube ich sparen. Probiers mal mit dem String obs damit klappt, vielleicht hat er das Quellencoding falsch erkannt bei den ganzen Möglichkeiten.

Das Fragezeichen ist der Replacementcharacter für nicht darstellbare Zeichen in UTF8. Wenn du die Originaldatei hast schau mal mit einem Hexeditor an die Stelle was da drin steht.

Ach ja: Willkommen in der wunderbaren Welt der Encodings, wo jeder Tag die Hölle und jede Datei eine neue Katastrophe ist...
 
Nope, der String hat nicht geholfen. Hier ist der HEX Editor.
hex11.png
EF BB BF Keine Ahnung wie das dort hin gekommen ist oder was es bedeutet. Ich KÖNNTE es einfach löschen, aber das wär ja zu einfach. Ich habe einen Post auf Stackoverflow gefunden.
http://stackoverflow.com/questions/1401317/remove-non-utf8-characters-from-string/3521396#3521396
Weiß nicht, ob das ein wenig overkill wäre... vielleicht morgen mal ausprobieren :)
 
Das ist aber die hochgeladene und umgewandelte Datei da im Hexeditor, oder? Was steht denn da in der Originaldatei, die du hochgeladen hast?
 
Ist das Bom nicht nur am Anfang der Datei? Also ich denk mal eher dass die Datei entweder schon Utf8 war bevor sie hochgeladen wurde, und da die Fragezeichen schon drin waren oder dass das gebundene Leerzeichen oder so ein Schrott waren. Ist halt schwer zu sagen ohne die Originaldatei...
 
Jop das BOM ist am Anfang der Datei. Ich kenne die Datei aus einem anderen Thread :D
Also einfach nach dem Upload das BOM generell entfernen und dann die Datei verarbeiten sollte gehen.
 
Zurück
Oben