Kirjautuminen

Haku

Tehtävät

Keskustelu: Nettisivujen teko: Kuvakanta ja pari kysymystä

Sivun loppuun

volume [24.09.2010 17:13:45]

#

hei,

minulla on oheinen koodinpätkä, joka tallettaa siis kuvan ja kuvan tiedot palvelimelle. siitä koodista olisi pari kysymystä:

1) voisiko/pitäisikö/täytyisikö käyttää transaktiota varmistamaan talletusta niin, että tapahtuma menee perille sekä tietokantaan että hakemistoon.

jos niin vinkkejä kuinka toteutan transaktion kätevästi?

2) tuleeko koodissa mielestänne ongelmia monen käyttäjän käyttäessä sitä mahdollisesti yhtäaikaa?

3) ja se vanha kysymys onko tietoturva-aukkoja jäänyt koodiin?

<?php

/***************************************************************/
/* Tallettaa halutun valokuvan infotiedot tietokantatauluun ja */
/* siirtaa kuvan maariteltyyn hakemistoon palvelimella         */
/***************************************************************/

// Avataan tietokantayhteydet

// Tarkastetaan onko tarvittavat syottotiedotannettu
if (($_FILES['tiedosto']['tmp_name']) and
    ($_POST['ryhma'] <> 'Valitse')    and
    ($_FILES['tiedosto']['size'] < 1000000))
    {

    // Haetaan seuraava vapaa id kannasta uudelle kuvalle
    function uusi_id() {
      global $yhteys;

       $q = "select MAX(id) from taulunnimi";
       $result = mysql_query($q);
       $data = mysql_fetch_array($result);
       return $data[0]+1;
    }

    $nextid = uusi_id();

    // Muodostetaan kuvalle uusi nimi
    $kuvanimi = "kuva" . $nextid . ".jpg";

    // Tarkastetaan syotekenttien data
    $kuvaus   =  mysql_real_escape_string(stripslashes($_POST['kuvaus']));
    $sanat    =  mysql_real_escape_string(stripslashes($_POST['sanat']));
    $paikka   =  mysql_real_escape_string(stripslashes($_POST['ryhma']));

    // Talletetaan kuvan tiedot tietokantaan
    $sql = "INSERT INTO taulunnimi (nimi, osoite, kuvaus, sanat, paikka, pvm)
             VALUES ('$kuvanimi','$osoite','$kuvaus','$sanat','$paikka',CurDate())";

    if (!mysql_query($sql,$yhteys))
       {
          $ilmoitus = "Talletus ei onnistunut!";
       }
       else
       {
          // Määritetään polku johon kuva talletetaan levyjärjestelmässä
          $polku = "/polku/johon/kuva/talletetaan/$kuvanimi";

          // Siirretään tiedosto kovalevylle
          if(move_uploaded_file($_FILES['tiedosto']['tmp_name'], $polku))
             {
                $ilmoitus = "Talletus onnistui!";
             }
             else
             {
                switch ($_FILES['uploadFile'] ['error'])
                   {
                      case 1:
                      $ilmoitus = "Kuvatiedosto on suurempi kuin PHP-perustiedot sallivat";
                      break;
                      case 2:
                      $ilmoitus = "Tiedosto on suurempi kuin form sallii!";
                      break;
                      case 3:
                      $ilmoitus = "Vain osa tiedostosta siirrettiin!";
                      break;
                      case 4:
                      $ilmoitus = "Tiedoston siirto ei onnistunut!";
                      break;
                   }  // switch...
             }     // if (move_uploadded...
       }

    }
    else
    {
       $ilmoitus = "Virhe: Syöttötiedot ovat puutteelliset!";
    }


// Suljetaan tietokantayhteys


?>

-tossu- [24.09.2010 18:05:23]

#

volume kirjoitti:

1) voisiko/pitäisikö/täytyisikö käyttää transaktiota varmistamaan talletusta niin, että tapahtuma menee perille sekä tietokantaan että hakemistoon.

Mielestäni on transaktioiden käyttö on turhaa, sen sijaan tiedot kannattaisi laittaa tietokantaan vasta kun tiedosto on tallennettu levylle onnistuneesti. Nykyisessä ratkaisussa kuvan tiedot jäävät tietokantaan, vaikka tallennus epäonnistuu.

volume kirjoitti:

2) tuleeko koodissa mielestänne ongelmia monen käyttäjän käyttäessä sitä mahdollisesti yhtäaikaa?

Mikäli kaksi käyttäjää lisää kuvan samaan aikaan, voi uusi_id antaa molemmille saman ID:n. ID kannattaisi toteuttaa AUTO_INCREMENT:illä.

volume kirjoitti:

3) ja se vanha kysymys onko tietoturva-aukkoja jäänyt koodiin?

En löytänyt nopealla tarkistuksella koodista tietoturva-aukkoja.

Metabolix [24.09.2010 18:08:41]

#

Vastaan käänteisessä järjestyksessä:

3) Tietoturva näyttäisi ainakin nopeasti katsoen olevan kunnossa.

2) Kyllä, koodi hajoaa, jos käyttäjiä on monta. Tämä johtuu tuosta id-purkkaviritelmästäsi: nythän kaksi samanaikaista käyttäjää voi saada saman "vapaan" id:n. Oikea tapa on ensin lisätä kantaan rivi ja sitten selvittää lisätyn rivin id funktiolla mysql_insert_id.

1) Toki voit käyttää transaktioita, jos haluat. Minusta kuitenkin on perusteltua olettaa, että tietokanta toimii, joten ainoat varsinaiset riskitekijät ovat levytilan loppuminen, jota ei toki pitäisi päästää tapahtumaan, ja käyttäjän keskeytys, jonka voi (ja kannattaakin) estää funktiolla ignore_user_abort.

Koodissasi on kuitenkin virhe, jota et ole tainnut edes ajatella: lisäät ensin tietokantarivin ja vasta sitten tarkistat tiedoston. Kantaan tulee siis rivi, vaikka lataus olisi täysin pielessä. Järkevä järjestys olisi seuraava:

<?php
$ilmoitus = hoida_upload($_FILES['uploadFile']);

function hoida_upload($f) {
  // Jos tiedoston lataus onnistui:
  if ($f['error'] == UPLOAD_ERR_OK) {
    ignore_user_abort(true);
    $dir = "/jokin/hakemisto";

    // Siirretään tiedosto oikeaan hakemistoon mutta väliaikaisella nimellä.
    // Tämän tarkoitus on varmistaa levytilan riittävyys jo ennen tietokannan
    // muokkaamista; jos riskiä ei ole, on toki tehokkaampaa jättää tämä pois
    // ja siirtää tiedosto suoraan lopulliselle paikalleen (ks. alempana).
    $tmp = uniqid("{$dir}/tmp_".getmypid().'_', true);
    if (!move_uploaded_file($f['tmp_name'], $tmp)) {
      return "Virhe tiedoston siirtämisessä!";
    }

    // Lisätään rivi kantaan.
    if (!mysql_query("INSERT ...")) {
      unlink($tmp);
      return "Virhe tietokannan kanssa!";
    }
    $id = mysql_insert_id();
    $nimi = "{$dir}/kuva_{$id}.jpg";

    // Siirretään tiedosto lopulliseen paikkaan.
    // Tässä ei pitäisi sattua virhettä, koska kyse on vain nimen vaihtamisesta.
    if (!rename($tmp, $nimi)) {
      unlink($tmp);
      return "Virhe tiedoston siirtämisessä!";
    }
    return "OK!";
  }
  // Tarkistetaan muut virheet.
  // Parempi käyttää nimettyjä vakioita kuin numeroita!
  // https://www.php.net/manual/en/features.file-upload.errors.php
  switch ($f['error']) {
    case UPLOAD_ERR_NO_FILE:
      return "Tiedosto puuttuu!";
    case UPLOAD_ERR_PARTIAL:
      return "Lataus jäi kesken!";
  }
  // Jotain muuta outoa sattui.
  return "Tuntematon virhe!";
}

Kuten huomaat, funktion käyttö myös selventää koodia, kun virhetilanteessa voi palauttaa viestin returnilla eikä tarvitse enempää sisäkkäisiä ehtolauseita.

Muuten, kuvan polkua ei tarvitse tallentaa tietokantaan, koska sen voi kuitenkin päätellä id:n perusteella. Tallentamisesta on vain harmia, jos päätätkin myöhemmin siirtää kuvat toiseen hakemistoon.

volume [25.09.2010 11:25:42]

#

Järkeilyni mukaan tarvitaan kuvatiedoston uudelleen nimeämisen jälkeen myös
koodi, joka nimeää kannassa olevan tietueen vastaavasti (vai olenko taas ajatellut päin honkia ;-)

<?php
// Siirretään tiedosto lopulliseen paikkaan.
    // Tässä ei pitäisi sattua virhettä, koska kyse on vain nimen vaihtamisesta.
    if (!rename($tmp, $nimi)) {
      unlink($tmp);
      return "Virhe tiedoston siirtämisessä!";
    }

// Päivitetään kantaan tietueelle vastaava nimi
    $tietue_nimi = "kuva{$id}.jpg";
    if (!mysql_query("UPDATE valokuvat set nimi   = '$tietue_nimi',
                                                     where id =$id")) {
      return "Virhe tietueen uudelleennime&auml;misess&auml;!";
    }

?>

Metabolix [25.09.2010 12:27:28]

#

Luitko loppuun? Sanoin juuri, että tuota nimeä ei tarvitse tallentaa kantaan ollenkaan, koska sen voi milloin tahansa generoida uudestaan id:n perusteella.

Metabolix kirjoitti:

Muuten, kuvan polkua ei tarvitse tallentaa tietokantaan, koska sen voi kuitenkin päätellä id:n perusteella. Tallentamisesta on vain harmia, jos päätätkin myöhemmin siirtää kuvat toiseen hakemistoon.

volume [25.09.2010 13:38:44]

#

jos mulla on levyllä kuva (nimenä kuvaX.jpg, missä X=kokonaisluku), niin miten ihmeessä yhdistän kuvan ja tietokannan tietueen ilman kuvan nimeä? kannassa on toki id, mutta levyllä vain kuvan nimi.

vai ajatteletko niin, että kuvan nimestä saadaan otettua irti id, jolla taas saadaan tietue kiinni (periaatteella 'kuva'-tekstin jälkeen ennen pistettä olevat merkit vastaavat kannassa tietueen id:tä).

miksi vierastat kuvan nimen talletusta tietueelle?

Teuro [25.09.2010 13:53:55]

#

No jos kuvan nimi on Kuva[id].jpg, niin päädyt oikeaan kuvaan tulostamalla kuvan seuraavasti:

<?php
/** Hakee kategorian 16 kuvat **/
$kuvat = mysql_query("SELECT id FROM kuvat WHERE category = 16");

while ($kuva = mysql_fecth_array($kuvat)) {
    echo "<p><img src=\"kuvat/Kuva{$kuva['id']}\" /></p>\r\n";
}
?>

Niin et tarvitse sitä nimeä yhtään mihinkään. Sen tallentamisesa on vain haittaa, koska jos tosiaan siirrtä kuvat jonnekin muualle tulisi sinun muokata jokainen tietokannan rivi, jotta toiminto ei hajoaisi. Toisaalta nyt piisaa kun muuttaa tuon yhden ainoan rivin oikeaksi.

Metabolix [25.09.2010 13:53:58]

#

Johan minä selitin, mainitsin jopa mahdollisen ongelman. Mitä kohtaa et ymmärtänyt? Entä keksitkö itse yhdenkin syyn, miksi tuo nimi pitäisi tallentaa?

Muunnos id:n ja nimen välillä toimii näinkin helposti:

<?php
function kuvan_nimi($id) {
  return "kuvat/kuva{$id}.jpg";
}
function kuvan_id($nimi) {
  return intval(preg_replace('/.*kuva([0-9]+).*/s', '\\1', $nimi));
}

Jälkimmäistä funktiota ei minun nähdäkseni pitäisi edes tarvita, koska eihän kenenkään kuulu pyytää tietoa kuvasta kuva123.jpg vaan kuvasta numero 123. Aivan kuten Teuron koodissa yllä: haetaan kannasta id, ja siitä jo tiedetään, mistä tiedosto löytyy.

volume [25.09.2010 13:58:22]

#

lainaus:

Johan minä selitin, mainitsin jopa mahdollisen ongelman.

jos tarkoitus tällä foorumilla ihan rakentavassa hengessä saada viisaammilta neuvoja ja vinkkejä, niin kannattaisi joskus kiinnittää huomiota siihen miten asiat formuloi.

jos ei jaksa "toistaa" paras olla vastaamalla lainkaan

ystävällisin terveisin: yks toope koodari volume

Metabolix [25.09.2010 14:13:48]

#

Jos tarkoitus on saada neuvoja ja vinkkejä, kannattaisi varmaan lukeakin ne neuvot ja vinkit. Toistetaan nyt sitten vielä kerran:

Metabolix kirjoitti:

Muuten, kuvan polkua ei tarvitse tallentaa tietokantaan, koska sen voi kuitenkin päätellä id:n perusteella. Tallentamisesta on vain harmia, jos päätätkin myöhemmin siirtää kuvat toiseen hakemistoon.

Jos tässä on jotain epäselvää, kysy toki. Ei kuitenkaan auta kysyä uudestaan täsmälleen samaa asiaa, vaan kysymyksiä pitää suunnata täsmällisemmin. Siksi kysyinkin vastaan, mitä kohtaa et ymmärtänyt.

volume [25.09.2010 15:32:09]

#

tässä taisin jäädä vain kiinni ajatukseen, että kuvanimi olisi levyä ja kantaa yhdistävä asia. on ehdottomasti parempi tehdä se näin kun olet/olette kertoneet.

antamassasi koodiesimerkissä yllä oli niin valtavan paljon uutta asiaa, että kesti heetken ennenkuin pääsin sen kanssa sinuksi.ja toinen hieman sekottava asia oli se, että käytit koodiesimerkissä funktiota -olen tottunut (sen vähän minkä olen tehnyt) käyttämään if-else lohkoja.

mutta eiköhän tämä tullut selväksi kantapään kautta (toivottavasti joku muukin hyötyy).

volume [25.09.2010 17:20:23]

#

ihan varmistus vielä

halutun kuvan nappaan taulukosta seuraavasti? (ainakin se toimii ilman herjoja):

<?php
<td><a href=\"kuva".$mysql_tiedot["id"].".jpg"."\">Katso</a></td>
?>php

misköhän tää sivu jättää \-viivan pois kuva sanan edestä

-tossu- [25.09.2010 18:53:28]

#

volume kirjoitti:

halutun kuvan nappaan taulukosta seuraavasti?

Et ainakaan tuolla koodilla saa sitä toimimaan, lainausmerkit ja escapetus on tehty aivan väärin, echokin puuttuu. Tämän pitäisi toimia:

<?php
echo "<td><a href=\"kuva" . $mysql_tiedot ["id"] . ".jpg\">Katso</a></td>";

Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta