sql >> Database teknologi >  >> RDS >> Mysql

php hvordan man linker en fil fra filserveren til den information fra databasen

Jeg regnede med, at jeg ville skrive et kort (for mig er dette kort) "svar" bare så jeg kunne opsummere mine pointer.

Nogle "bedste fremgangsmåder", når du opretter et fillagringssystem. Fillagring er en bred kategori, så din kilometertal kan variere for nogle af disse. Tag dem bare som et forslag til, hvad jeg fandt, der fungerer godt.

Filnavne Gem ikke filen med det navn, som en slutbruger giver den. De kan og vil bruge alle slags lorte karakterer, der vil gøre dit liv surt. Nogle kan være så dårlige som ' enkelte citater, hvilket på linux dybest set gør det umuligt at læse, eller endda slette filen (direkte). Nogle ting kan virke simple som et mellemrum, men afhængigt af hvor du bruger det og operativsystemet på din server kan du ende med one%20two.txt eller one+two.txt eller one two.txt som måske eller måske ikke skaber alle slags problemer i dine links.

Den bedste ting at gøre er at oprette en hash, noget som sha1 dette kan være så simpelt som {user_id}{orgianl_name} Brugernavnet gør det mindre sandsynligt for kollisioner med andre brugeres filnavne.

Jeg foretrækker at lave file_hash('sha1', $contents) på den måde, hvis nogen uploader den samme fil mere, så når du kan fange det (indholdet er det samme, hashen er den samme). Men hvis du forventer at have store filer, vil du måske lave nogle bench-markering på den for at se, hvilken type ydeevne den har. Jeg håndterer mest små filer, så det fungerer fint til det.-bemærk- at med tidsstemplet kan filen stadig gemmes, fordi det fulde navn er anderledes, men det gør det ret nemt at se, og det kan verificeres i databasen.

Uanset hvad du gør, ville jeg præfikse det med et tidsstempel time().'-'.$filename . Dette er nyttig information at have, fordi det er det absolutte tidspunkt, hvor filen blev oprettet.

Hvad angår navnet, giver en bruger filen. Bare gem det i databaseposten. På denne måde kan du vise dem det navn, de forventer, men brug et navn, som du ved altid er sikkert for links.

$filename ='noget lort^ fileane.jpg';

$ext = strrchr($filename, '.');

echo "\nExt: {$ext}\n";

$hash = sha1('some crapy^ fileane.jpg');

echo "Hash: {$hash}\n";

$time = time();

echo "Timestamp: {$time}\n";

$hashname = $time.'-'.$hash.$ext;

echo "Hashname: $hashname\n";

Udgange

Ext: .jpg
Hash: bb9d2c2c7c73bb8248537a701870e35742b41c02
Timestamp: 1511853063
Hashname: 1511853063-bb9d2c2c7c73bb8248537a701870e35742b41c02.jpg

Du kan prøve det her

Stier gem aldrig den fulde sti til filen. Alt hvad du behøver i databasen er hashen fra oprettelsen af ​​det hash-navn. "Root"-stien til den mappe, filen er gemt i, skal udføres i PHP. Dette har flere fordele.

  • forhindrer katalogoverførsel. Fordi du ikke passerer nogen del af stien rundt om dig, behøver du ikke bekymre dig så meget om, at nogen smutter en \..\.. derinde og går steder, de ikke burde. Et dårligt eksempel på dette ville være nogen, der overskriver et .htpassword fil ved at uploade en fil ved navn den med mappe på tværs.
  • Har mere ensartede links, ensartet størrelse, ensartet sæt af tegn.

https://en.wikipedia.org/wiki/Directory_traversal_attack

  • Vedligeholdelse. Stier ændrer sig, servere ændrer sig. Kravene til dit system ændrer sig. Hvis du har brug for at flytte disse filer, men du har gemt den absolutte fulde sti til dem i DB'en, sidder du fast ved at lime alt sammen med symlinks eller opdatere alle dine optegnelser.

Der er nogle undtagelser fra dette. Hvis du vil gemme dem i en månedlig mappe eller efter brugernavn. Du kan gemme den del af stien i et separat felt. Men selv i det tilfælde kan du bygge det dynamisk baseret på data gemt i posten. Jeg har fundet ud af, at det er bedst at gemme så lidt stioplysninger som muligt. Og de laver en konfiguration eller en konstant, som du kan bruge alle de steder, du skal bruge for at sætte stien til filen.

Også path og link er meget forskellige, så ved kun at gemme navnet kan du linke det fra hvilken PHP-side du ønsker uden at skulle trække data fra stien. Jeg har altid fundet det nemmere at tilføje til filnavnet og derefter trække fra en sti.

Database (kun nogle forslag, brugen kan variere) Som altid med data spørg dig selv, hvem, hvad, hvor, hvornår

  • id - int primær nøgle automatisk stigning
  • bruger_id - int fremmednøgle, hvem uploadede den
  • hash - char[40] *sha1*, unique hvad hashen
  • hashnavn - varchar {timestampl}-{hash}.{ext} hvor filnavnet på harddisken
  • filnavn - varchar det originale navn givet af brugeren, på den måde kan vi vise dem det navn, de forventer (hvis det er vigtigt)
  • status - enum[public,private,deleted,pending.. etc] status for filen, afhængigt af dit brugstilfælde, skal du muligvis gennemgå filerne, eller måske er nogle private, kun brugeren kan se dem, måske er nogle offentlige osv.
  • status_dato - timestamp|datetime tidspunkt, hvor status blev ændret.
  • create_date - timestamp|datetime hvornår tidspunkt, hvor filen blev oprettet, foretrækkes et tidsstempel, da det gør nogle ting nemmere, men det bør i så fald være det samme tidsstempel, som bruges i hashnavnet.
  • type - varchar - mime-type, kan være nyttig til at indstille mime-typen ved download osv.

Hvis du forventer, at forskellige brugere uploader den samme fil, og du bruger file_hash du kan lave hash felt et kombineret unikt indeks for user_id og hash på denne måde ville det kun være i konflikt, hvis den samme bruger uploadede den samme fil. Du kan også gøre det baseret på tidsstemplet og hash, afhængigt af dine behov.

Det er de grundlæggende ting, jeg kunne komme i tanke om, dette er ikke et absolut, bare nogle felter, jeg troede ville være nyttige.

Det er nyttigt at have hashen for sig selv, hvis du gemmer den for sig selv, kan du gemme den i en CHAR(40) for sha1 (optager mindre plads i DB'en end VARCHAR ) og indstil sorteringen til UTF8_bin som er binært. Dette gør søgninger på det store og små bogstaver. Selvom der er ringe mulighed for en hash-kollision, tilføjer dette bare en smule mere beskyttelse, fordi hashes er store og små bogstaver.

Du kan altid bygge hashname on the fly, hvis du gemmer udvidelsen, og tidsstemplet adskilt. Hvis du finder dig selv at skabe ting igen og igen, vil du måske bare gemme det i DB'en for at forenkle arbejdet i PHP.

Jeg kan godt lide bare at sætte hashen i linket, ingen udvidelse ingen noget, så mine links ser sådan ud.

http://www.example.com/download/ad87109bfff0765f4dd8cf4943b04d16a4070fea

Rigtig enkel, ægte generisk, sikker i urls altid samme størrelse osv..

hashname for denne "fil" ville være sådan her

1511848005-ad87109bfff0765f4dd8cf4943b04d16a4070fea.jpg

Hvis du har konflikter med den samme fil og en anden bruger (som jeg nævnte ovenfor). Du kan altid tilføje tidsstemplet til linket, user_id eller begge dele. Hvis du bruger user_id'et, kan det være nyttigt at venstre indsætte det med nuller. For eksempel kan nogle brugere have ID:1 og nogle kan være ID:234 så du kunne lade den ligge på 4 steder og gøre dem til 0001 og 0234 . Føj derefter det til hashen, hvilket næsten ikke er til at bemærke:

1511848005-ad87109bfff0765f4dd8cf4943b04d16a4070fea0234.jpg

Det vigtige her er, at fordi sha1 er altid 40 og id'et er altid 4 vi kan adskille de to præcist og nemt. Og på denne måde kan du stadig slå det unikt op. Der er mange forskellige muligheder, men så meget afhænger af dine behov.

Adgang Såsom at downloade. Du bør altid udlæse filen med PHP, giv dem ikke direkte adgang til filen. Den bedste måde er at gemme filerne uden for webroot (over public_html). eller www mappe). Så i PHP kan du indstille overskrifterne til den korrekte type og grundlæggende læse filen op. Dette virker til stort set alt undtagen video. Jeg håndterer ikke videoer, så det er et emne uden for min erfaring. Men jeg synes, det er bedst at tænke på det, da alle fildata er tekst, det er de overskrifter, der gør teksten til et billede eller en excel-fil eller en pdf.

Den store fordel ved ikke at give dem direkte adgang til filen er, at hvis du har et medlemsside, eller hvis du ikke vil have dit indhold tilgængeligt uden login, kan du nemt tjekke i PHP, om de er logget ind, før du giver dem indholdet. Og da filen er uden for webroot, kan de ikke få adgang til den på anden måde.

Det vigtigste er at vælge noget konsekvent, som stadig er fleksibelt nok til at klare alle dine behov.

Jeg er sikker på, at jeg kan komme med mere, men hvis du har nogle forslag, er du velkommen til at kommentere.

GRUNDLÆGGENDE PROCESFLOW

  1. Brugeren indsender formular (enctype="multipart/form-data" )

https://www.w3schools.com/tags/att_form_enctype.asp

  1. Serveren modtager indlægget fra formularen Super Globals $_POST og $_FILES

http://php.net/manual/en/reserved.variables.files .php

$_FILES = [
 'fieldname' => [
        'name' => "MyFile.txt" // (comes from the browser, so treat as tainted)
        'type' => "text/plain" //  (not sure where it gets this from - assume the browser, so treat as tainted)
        'tmp_name' => "/tmp/php/php1h4j1o" // (could be anywhere on your system, depending on your config settings, but the user has no control, so this isn't tainted)
        'error' => "0" //UPLOAD_ERR_OK  (= 0)
        'size' => "123" //   (the size in bytes)
    ]
 ];
  1. Tjek for fejl if(!$_FILES['fielname']['error'])

  2. Rengør visningsnavnet $filename = htmlentities($str, ENT_NOQUOTES, "UTF-8");

  3. Gem fil, opret DB-record ( PSUDO-CODE )

Sådan:

 $path = __DIR__.'/uploads/'; //for exmaple

$time = time();
$hash = hash_file('sha1',$_FILES['fielname']['tmp_name']);
$type = $_FILES['fielname']['type'];
$hashname = $time.'-'.$hash.strrchr($_FILES['fielname']['name'], '.');
$status = 'pending';

if(!move_uploaded_file ($_FILES['fielname']['tmp_name'], $path.$hashname  )){
     //failed
     //do somehing for errors.
     die();
}


//store record in db

http://php.net/manual/en/function.move -uploaded-file.php

  1. Opret link (varierer baseret på routing), den enkle måde er at lave dit link på denne måde http://www.example.com/download?file={$hash} men det er grimmere end http://www.example.com/download/{$hash}

  2. bruger klikker på linket går til downloadsiden.

få INPUT og slå posten op

$hash = $_GET['file'];

$stmt = $PDO->prepare("SELECT * FROM attachments WHERE hash = :hash LIMIT 1");  
$stmt->execute([":hash" => $hash]);

$row = $stmt->fetch(PDO::FETCH_ASSOC);

print_r($row);

http://php.net/manual/en/intro.pdo.php

Osv...

Skål!




  1. Sådan filtreres rækker med null-værdier i Select Statement i SQL Server - SQL Server / TSQL Tutorial Del 110

  2. hvordan man kontrollerer, om en streng ser tilfældig ud, eller menneskeskabt og udtalelig?

  3. Sådan ændres dato- og tidsformater i T-SQL

  4. INDSÆT I Tabel fra flere tabeller