Usamos cookies para medir audiência e melhorar sua experiência. Você pode aceitar ou recusar a qualquer momento. Veja sobre o iMasters.
Olá, minha dúvida agora é coisa simples: meu site que estou fazendo terá um sistema de uploads de imagens e eu gostaria de saber, qual o melhor método para armazenar estas imagens?
Qual método me recomendam? Qual a vantagem do mesmo sobre o outro?
Até mais. :D
Armazenar os arquivos em um diretório e apenas o nome do arquivo no banco de dados é a melhor opção.
A outra opção tem as seguintes desvantagens:
Como diriam os Titãs: o fácil é o certo. A solução mais simples é sempre a melhor.
A seguir, uma lista de vantagens e desvantagens que elaborei de cada método.
Vantagens do método Base64:
Vantagens do método tradicional:
Desvantagens do método Base64:
Desvantagens do método tradicional:
Então, analisando isso ae, qual é realmente vantajoso? Fiquei confuso, e podem haver outras vantagens/desvantagens que deixei passar.
É muito fácil impedir o hotlinking de suas imagens através do htaccess.
Exemplo:
RewriteEngine on
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{REQUEST_FILENAME} -f
RewriteCond %{REQUEST_FILENAME} \.(gif|jpe?g?|png)$ [NC]
RewriteCond %{HTTP_REFERER} !^https?://([^.]+\.)?seu-dominio\.com\.br [NC]
RewriteRule \.(gif|jpe?g?|png)$
Pois é, mas desse jeito ae, vou estar impedindo de todas imagens ao mesmo tempo. E se eu quiser bloquear apenas algumas? Não posso ficar editando htaccess direto, tem que ser tudo automático.
Ah, aproveitando que tamo falando de hotlink (que nem é bem o foco do tópico): se eu bloquear, vai impedir indexação pelo Google por exemplo?
>
Pois é, mas desse jeito ae, vou estar impedindo de todas imagens ao mesmo tempo. E se eu quiser bloquear apenas algumas? Não posso ficar editando htaccess direto, tem que ser tudo automático.
É só redirecionar para um arquivo PHP então...
>
Ah, aproveitando que tamo falando de hotlink (que nem é bem o foco do tópico): se eu bloquear, vai impedir indexação pelo Google por exemplo?
Bom... você que falou que só era possível ter controle sobre o acesso a image e impedir que outros usem sem permissão só seria possível se a imagem estivesse no banco de dados. Eu discordo. Existem muitas maneiras de prevenir hotlinking de imagens armazenadas no sistema de arquivos. Obviamente que sempre é possível burlar essas proteções, mas essa burla também vale para o armazenamento da imagem no banco de dados.
Creio que irá acontecer o seguinte: o Google irá criar o thumbnail da imagem e indexar normalmente, mas quando a imagem aparecer nos resultados de uma busca e o usuário clicar nela ele receberá um erro 404 ou um erro forbidden ou o que mais você programar para quando alguém tentar fazer hotlinking com a sua imagem. Já me aconteceu isso algumas vezes... é frustrante e faz o usuário perder tempo, portanto, se você for fazer isso, é melhor já bloquear logo o Googlebot-Images no seu robots.txt.
Não acho que vai dar erro 404, já que o link vai apontar para a imagem original, no meu site mesmo, ela não vai estar em outro site, então não será hotlinking...
Sobre a hospedagem ae: eu acho que vou armazenar no banco de dados somente imagens que poderão ser alteradas a qualquer momento, ex: o avatar de um usuário, ele pode mudar a qualquer momento, e acho que seria mais fácil só alterar o Base64 da imagem no banco de dados, do que deletar a imagem atual no disco, e colocar a nova no lugar.... Imagens fixas, do layout, postagens, etc... vou armazenar no método tradicional mesmo, já que serão fixas, não serão alteradas, não por outras pessoas.
Vlw ae :D
>
envie o nome da imagem para o banco de dados.
exemplo de um script que eu tenho aqui:
function trocar_acentos ($imagem)
{
$imagem = str_replace(' ','_',$imagem);
$imagem = str_replace('á','a',$imagem);
$imagem = str_replace('ã','a',$imagem);
$imagem = str_replace('â','a',$imagem);
$imagem = str_replace('ä','a',$imagem);
$imagem = str_replace('à','a',$imagem);
$imagem = str_replace('Á','a',$imagem);
$imagem = str_replace('À','a',$imagem);
$imagem = str_replace('Ã','a',$imagem);
$imagem = str_replace('Â','a',$imagem);
$imagem = str_replace('Ä','a',$imagem);
$imagem = str_replace('ç','c',$imagem);
$imagem = str_replace('Ç','c',$imagem);
$imagem = str_replace('é','e',$imagem);
$imagem = str_replace('É','e',$imagem);
$imagem = str_replace('í','i',$imagem);
$imagem = str_replace('Í','i',$imagem);
$imagem = str_replace('ó','o',$imagem);
$imagem = str_replace('Ó','o',$imagem);
$imagem = str_replace('ú','u',$imagem);
$imagem = str_replace('Ú','u',$imagem);
$imagem = strtolower($imagem);
return $imagem;
}
$imagem = trocar_acentos ($_FILES['imagem']['name']);
$imagem_extensao = substr($imagem,strpos($imagem,'.')+1,strlen($imagem)-strpos($imagem,'.'));
$imagem_tamanho = $_FILES['imagem']['size'];
$imagem_descricao = $_POST['imagem_descricao'];
$imagem_data = date_default_timezone_set('America/Sao_Paulo');
$uploaddir = "/uploads/";
$data = time();
if ($imagem != ""){
if (file_exists($uploaddir.$imagem))
{
$imagem = $data."_".$imagem;
}
}
move_uploaded_file($_FILES['imagem']['tmp_name'], $uploaddir . $imagem);
$query = "insert into uploads (imagem) values ('$imagem')";
$rs = mysql_query($query);
precisa ser aprimorado, mas ja da pra começar :)
qualquer modificação poste ai :D
O cara nem leu a pergunta...
Em MySQL, desencana. Local físico, no banco você guarda só o caminho. Se for guardar o conteúdo das imagens em banco, sugiro Postgres ou alugm NoSQL como MongoDB.
No MySQL, desencana mesmo, alias, eu sou totalmente contra gravar imagens em um banco de dados, por N motivos, eu prefiro deixar no FS.
Eu só guardaria imagens no banco, se por algum motivo em especial eu não tiver permissão de escrita em um diretório de armazenamento (talvez nunca aconteça, mas ...), no banco é totalmente inviável porque:
Se você possui select com relacionamentos, e alteração dinâmica na tabela, ou um acesso concorrente significante, você vai ter um trafego muito grande, e suas consultas podem demorar.
Pensando em 10.000 registros, você já começaria com alguns problemas, e claro sem falar no tamanho do buffer de saída na página, que ia ficar ENORME dependendo da quantidade de registros.
Outra que o propósito do banco não é bem esse, os campos BLOB, LONGBLOB, podem ser usados para esse caso, mas iria cair na mesma questão das queries, vai ser lento, e eu sinceramente acho, que isso é um erro de modelagem.
Obrigado a todos pelas respostas.
Bom, eu apenas pensei em salvar o conteúdo da imagem no banco de dados por ser muito mais fácil... eu apenas codificaria ele em base64 e pronto, salvaria no db, porém notei que assim usa 33% a mais de espaço em disco, segundo a página da função base64_encode no php.net.
Olhando o script do lightfox dei uma pesquisada sobre a variável $_FILES e a função move_uploaded_file e deu pra entender como fazer o upload normal, eu nunca precisei usar isso, então achei que era mais complicado, kkk.
Então, vou usar o método normal mesmo, até porque vocês crucificaram ae o outro, hehe.
Só mais uma coisa: o Evandro falou ae que seria melhor no Postgree ou algum NoSQL... porque eles sim, e o MySQL não? MySQL só serve para pequenas strings? Se for, vou ter que migrar para Postgree... estava pretendendo salvar o código fonte das postagens no banco de dados, e isso poderia ser bem grande...
>
Obrigado a todos pelas respostas.
Bom, eu apenas pensei em salvar o conteúdo da imagem no banco de dados por ser muito mais fácil... eu apenas codificaria ele em base64 e pronto, salvaria no db, porém notei que assim usa 33% a mais de espaço em disco, segundo a página da função base64_encode no php.net.
Olhando o script do lightfox dei uma pesquisada sobre a variável $_FILES e a função move_uploaded_file e deu pra entender como fazer o upload normal, eu nunca precisei usar isso, então achei que era mais complicado, kkk.
Então, vou usar o método normal mesmo, até porque vocês crucificaram ae o outro, hehe.
Só mais uma coisa: o Evandro falou ae que seria melhor no Postgree ou algum NoSQL... porque eles sim, e o MySQL não? MySQL só serve para pequenas strings? Se for, vou ter que migrar para Postgree... estava pretendendo salvar o código fonte das postagens no banco de dados, e isso poderia ser bem grande...
A questão não é bem o tamanho e sim o tipo. MySQL é ótimo e muito performático armazenando longas cadeias de caracteres. Mas estamos falando de texto plano. Postgres cairia no mesmo problema, a ressalva é que a gestão de conteúdo binário nos campos *BLOB é otimizada neste banco.
NoSQL já é um outro ecossistema. Não sei nem porque cismam em querer comparar com SGDB's relacionais.
@Evandro Oliveira, independente disso kra, eu acho errado.
Me diz um motivo pra gravar imagens no banco de dados.
>
@Evandro Oliveira, independente disso kra, eu acho errado.
Me diz um motivo pra gravar imagens no banco de dados.
Acesso, minimização de leitura, menos requisições de memória, apenas um ponteiro, menos recursos de processamento.
Imagine que o disco é um grande - enorme - corredor contendo arquivos. Seu diretório /images fica no começo desse corredor. Em um gaveta, em um dos primeiros arquivos.
Seu banco de dados, armazena o conteúdo - índices, tabelas, valores - lá pelo meio/final do corredor. Quem espera esses dados, te espera depois do final do corredor.
Você vai até o meio, para procurar em algum registro onde se encontra a imagem física, descobre, volta até o começo, pega a imagem e leva até o fim do corredor.
Veja: busca -> leitura -> envio -> busca -> envio
Armazenando tudo em um único local, podemos trabalhar um único sistema de caching, otimizar um único "sistema de arquivos" e fazer apenas um acesso ao disco
busca -> envio
Certo, agora um bench que eu fiz:
<?php
$m = new Mongo();
$db = $m->bench ;
/*$content = file_get_contents('jQuery.jpg');
$content = base64_encode($content);
for($i = 0 ; $i < 1000 ; ++$i){
$db->images->insert(array(
"i" => $i,
"contentEncoded" => $content
));
}*/
$s = microtime(true);
foreach($db->images->find() as $image){
var_dump($image);
}
$e = (microtime(true) - $s);
var_dump($e);
Inseri, depois comentei o código, e fiz a leitura ...
float(70.329179048538)
Esse resultado para 1000 copias de uma imagem de 128KB (nem todas terão 128KB, talvez você faça resize, talvez guarde a original e o thumb).
Daí depois tenta apertar CTRL+U.
Não vale apena ... sinceramente, você gasta alguns segundos levando em consideração o que o Evandro falou, é mais em conta do que gravar isso no banco (eu acho).
Se quiser fazer o bench sem usar o DB, simples, pega uma imagem de 128KB codifica o base 64, depois faz o looping exibindo o conteúdo..
<?php
$content = file_get_contents('jQuery.jpg');
$content = base64_encode($content);
$s = microtime(true);
for($i = 0; $i < 1000; ++$i){
echo $content ;
}
$e = (microtime(true) - $s);
var_dump($e);
Informe o resultado e o que você acha.
E ainda leva em consideração o tamanho do trafego de dados entre o servidor php e o banco de dados.
E lembre da concorrência, suponha uns 20 - 30 usuários, claro que você não vai trazer 1000 imagens, pode limitar sua busca a 10 (talvez uma lista), ou coisa assim.
Para testar a concorrência, pode usar o apache benchmark.
Não dá pra fazer todos esses testes agora, mas....
Lembrando também, que isso também vai aumentar o tamanho do arquivo (caso o armazenamento das imagens for no banco), e como você citou um "caching", se você guarda por 5 minutos, e exibe pra 30 usuários, você vai ler um arquivo de no mínimo 3-4 MB a cada requisição.
E provavelmente, qualquer consulta a nodes do documento com jQuery, ou JS Puro, irá demorar (não tenho crtz, não fiz o teste)
@Evandro, uma pergunta pessoal minha agora, suponha que você tenha que desenvolver um eCommerce, e na página de detalhes do produto, você mostra aquele slide com imagens do produto, normalmente essas imagens são referenciadas, e você tem (200-300) produtos, e 5 imagens em cada, você guardaria isso tudo em um banco de dados (base64 encoded) e mostraria como data-uri ? ou guardaria a referencia delas no banco ? (MySQL/MongoDB ou Cassandra)
>
Certo, agora um bench que eu fiz:
<?php
$m = new Mongo();
$db = $m->bench ;
/*$content = file_get_contents('jQuery.jpg');
$content = base64_encode($content);
for($i = 0 ; $i < 1000 ; ++$i){
$db->images->insert(array(
"i" => $i,
"contentEncoded" => $content
));
}*/
$s = microtime(true);
foreach($db->images->find() as $image){
var_dump($image);
}
$e = (microtime(true) - $s);
var_dump($e);
Inseri, depois comentei o código, e fiz a leitura ...
float(70.329179048538)
Esse resultado para 1000 copias de uma imagem de 128KB (nem todas terão 128KB, talvez você faça resize, talvez guarde a original e o thumb).
Daí depois tenta apertar CTRL+U.
Não vale apena ... sinceramente, você gasta alguns segundos levando em consideração o que o Evandro falou, é mais em conta do que gravar isso no banco (eu acho).
Se quiser fazer o bench sem usar o DB, simples, pega uma imagem de 128KB codifica o base 64, depois faz o looping exibindo o conteúdo..
<?php
$content = file_get_contents('jQuery.jpg');
$content = base64_encode($content);
$s = microtime(true);
for($i = 0; $i < 1000; ++$i){
echo $content ;
}
$e = (microtime(true) - $s);
var_dump($e);
Informe o resultado e o que você acha.
E ainda leva em consideração o tamanho do trafego de dados entre o servidor php e o banco de dados.
E lembre da concorrência, suponha uns 20 - 30 usuários, claro que você não vai trazer 1000 imagens, pode limitar sua busca a 10 (talvez uma lista), ou coisa assim.
Para testar a concorrência, pode usar o apache benchmark.
Não dá pra fazer todos esses testes agora, mas....
Lembrando também, que isso também vai aumentar o tamanho do arquivo (caso o armazenamento das imagens for no banco), e como você citou um "caching", se você guarda por 5 minutos, e exibe pra 30 usuários, você vai ler um arquivo de no mínimo 3-4 MB a cada requisição.
E provavelmente, qualquer consulta a nodes do documento com jQuery, ou JS Puro, irá demorar (não tenho crtz, não fiz o teste)
Benchmark, por benchmark, fiz o meu, com Postgres.
SQL
CREATE TABLE with_blob (
id Serial primary key,
img OID
);
CREATE TABLE without_blob (
id Serial primary key,
img TEXT
);
CREATE TABLE with_path (
id Serial primary key,
img varchar(30)
);
Psql.php
<?php
class Psql extends PDO
{
CONST INSERT_WITH_BLOB
= "INSERT INTO with_blob(img) VALUES(lo_import(?));";
CONST SELECT_WITH_BLOB
= "SELECT img, lo_export(img, CONCAT('/tmp/', img))
FROM with_blob LIMIT 1";
CONST DELETE_WITH_BLOB = "DELETE FROM with_blob;";
CONST INSERT_WITHOUT_BLOB = "INSERT INTO without_blob(img) VALUES(?)";
CONST SELECT_WITHOUT_BLOB = "SELECT img FROM without_blob LIMIT 1";
CONST DELETE_WITHOUT_BLOB = "DELETE FROM without_blob;";
CONST INSERT_WITH_PATH = "INSERT INTO with_path(img) VALUES(?)";
CONST SELECT_WITH_PATH = "SELECT img FROM with_path LIMIT 1";
CONST DELETE_WITH_PATH = "DELETE FROM with_path";
CONST DSN = "pgsql:host=localhost;dbname=evandro";
CONST FILEPATH = "/tmp/imasters.png";
public function __construct()
{
parent::__construct(psql::DSN, 'evandro', 'root');
$this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
public function insertWithBlob()
{
$this->prepare(psql::INSERT_WITH_BLOB)->execute(array(psql::FILEPATH));
}
public function selectWithBlob()
{
return file_get_contents(
'/tmp/' . $this->query(psql::SELECT_WITH_BLOB)->fetchColumn()
);
}
public function deleteWithBlob()
{
$this->exec(psql::DELETE_WITH_BLOB);
}
public function insertWithoutBlob()
{
$img = base64_encode(file_get_contents(psql::FILEPATH));
$this->prepare(psql::INSERT_WITHOUT_BLOB)->execute(array($img));
}
public function selectWithoutBlob()
{
return base64_decode(
$this->query(psql::SELECT_WITHOUT_BLOB)->fetchColumn()
);
}
public function deleteWithoutBlob()
{
$this->exec(psql::DELETE_WITHOUT_BLOB);
}
public function insertWithPath()
{
$path = '/tmp/' . uniqid();
$this->prepare(Psql::INSERT_WITH_PATH)->execute(array($path));
copy(Psql::FILEPATH, $path);
}
public function selectWithPath()
{
return file_get_contents(
$this->query(Psql::SELECT_WITH_PATH)->fetchColumn());
}
public function deleteWithPath()
{
$this->exec(Psql::DELETE_WITH_PATH);
}
}
Arquivo de testes, para garantir que todas as respostas sejam idênticas
PsqlTest.php
<?php
require_once('Psql.php');
require_once('PHPUnit/Autoload.php');
class PsqlTest extends PHPUnit_Framework_TestCase
{
/** @var $instance Psql */
private $instance;
private $filedata;
protected function setUp()
{
$this->filedata = file_get_contents('/tmp/imasters.png');
$this->instance = new Psql();
}
public function testSelectWithBlob()
{
$this->instance->insertWithBlob();
$this->assertEquals($this->filedata,
$this->instance->selectWithBlob());
$this->instance->deleteWithBlob();
}
public function testSelectWithoutBlob()
{
$this->instance->insertWithoutBlob();
$this->assertEquals($this->filedata,
$this->instance->selectWithoutBlob());
$this->instance->deleteWithoutBlob();
}
public function testSelectWithPath()
{
$this->instance->insertWithPath();
$this->assertEquals($this->filedata,
$this->instance->selectWithPath());
$this->instance->deleteWithPath();
}
}
Para executar, tendo o PHPUnit instalado, basta por os dois arquivos no mesmo diretório e rodar um
phpunit PsqlTest.php
O arquivo de profile do XDebug: http://minus.com/mde2sEAxv
Você pode abrir este arquivo pelo NetBeans, acho que pelo Eclipse, pelo PHPStorm, se usar KDE pelo kcachegrind, se usar Windows pelo wincachegrind ou usando um browser com o webgrind
os resultados aqui na minha máquina foram o seguinte:
/applications/core/interface/imageproxy/imageproxy.php?img=http://files.myopera.com/EvandroOliveira/albums/11602162/insert.jpg&key=bab10df89b8db1388d7de7888e89145cc99207d5195b6cd38d75144b1de6f2c3" alt="insert.jpg" />
/applications/core/interface/imageproxy/imageproxy.php?img=http://files.myopera.com/EvandroOliveira/albums/11602162/select.jpg&key=0934f98277b908e4b8c9621baef60caf08af2533a98c5f3e970f31f92cd6fbc8" alt="select.jpg" />
/applications/core/interface/imageproxy/imageproxy.php?img=http://files.myopera.com/EvandroOliveira/albums/11602162/delete.jpg&key=dce7b437ae6064d46afc828a3304b6e8ee6a655e8e1810cf11b89829246a01df" alt="delete.jpg" />
>
@Evandro, uma pergunta pessoal minha agora, suponha que você tenha que desenvolver um eCommerce, e na página de detalhes do produto, você mostra aquele slide com imagens do produto, normalmente essas imagens são referenciadas, e você tem (200-300) produtos, e 5 imagens em cada, você guardaria isso tudo em um banco de dados (base64 encoded) e mostraria como data-uri ? ou guardaria a referencia delas no banco ? (MySQL/MongoDB ou Cassandra)
Escrever as imagens como data-uri está fora de cogitação. Nunca sacrificaria as várias formas de caching e compressão nesse cenário.
data-uri pra mim seria uma saída pra um fundo degradê, por exemplo. Não imagens complexas. Às vezes o base64 gerado consegue ser maior que a imagem! Sem contar que eu teria que embutir isso no markup, atrasando a exibição do conteúdo textual.
Se estamos falando de eCommerce, falamos de performance máxima, correto?? Eu optaria por uma lógica sequencial - o produto de ID 6, carregaria as imagens 6-1.jpg, 6-2.jpg, 6-3.jpg, 6-4.jpg e 6-5.jpg. E eu não procurei nada no banco. Eu sei que o ID é 6 pela URL da página. Essas imagens ficariam muito provavelmente numa rede CDN, num outro domínio, livre de cookies, servindo conteúdo compactado por gzip. Entenda, eu não toco em qualquer banco de dados para esse cenário. Porque é desnecessário.
Entretanto, no final das contas resume-se a não guardar imagens no banco de dados.
Estou errado ? você de qualquer forma tem o objetivo de exibi-la, se for por data-uri, você vai ter aquela string [0...12000] na sua saída, deixando o site todo lento.
Os tipos de campo eu nem discuto não, sei que com BLOB é melhor, é o tipo de campo correto para esse tipo de coisa.
Mas a questão era: É bom ou NÃO guardar imagens no banco de dados.
Sobre o acesso das informações do produto, veja bem, você sabe o ID do produto, mas não sabe quantas fotos são, ali eu e você afirmamos que são 5, mas e se não forem ? de qualquer forma você vai consultar o banco pra pegar as informações, o que eu faço nesse caso de várias imagens, é simplesmente uma coluna TEXT que vai ficar os paths delimitados por ';', na hora eu apenas quebro a string e exibo.
E Evandro, seu teste tá meio errado aí não ? eu não falei sobre tipos de campo, e sim sobre o conteúdo no banco que no final das contas resume-se a exibição dele (de qualquer forma referência ou não, vai cair na tag img).
No post #10, eu digo "os campos BLOB, LONGBLOB, podem ser usados para esse caso".
Logo em seguida, meu teste se baseia na exibição do conteúdo desses campos.
>
[...] o que eu faço nesse caso de várias imagens, é simplesmente uma coluna TEXT que vai ficar os paths delimitados por ';', na hora eu apenas quebro a string e exibo.
[...]
Desculpe me intrometer, mas não resisti...
Isso pode até ser mais rápido em termos de benchmark (não testei...), mas eu acho muito deselegante e pouco profissional. Eu crio logo um relacionamento (1hasMany) entre a tabela 'produtos' e a tabela 'imagens'. Só de pensar que na hora de adicionar uma imagem onde já haviam umas 20 eu terei que fazer a consulta e depois concatenar a string e por fim enviar de volta ao banco de dados para que o campo seja sobrescrito eu já fico agoniado.
>
Isso pode até ser mais rápido em termos de benchmark (não testei...), mas eu acho muito deselegante e pouco profissional. Eu crio logo um relacionamento (1hasMany) entre a tabela 'produtos' e a tabela 'imagens'. Só de pensar que na hora de adicionar uma imagem onde já haviam umas 20 eu terei que fazer a consulta e depois concatenar a string e por fim enviar de volta ao banco de dados para que o campo seja sobrescrito eu já fico agoniado.
Certo, da mesma forma que você vai precisar da consulta pra montar o formulário de update, você vai ter essa coluna já com ele.
E da mesma forma que você usa o explode pra poder separar os delimitadores e tem o array de retorno, você faz um push dessa imagem nesse array e um implode com o delimitador novamente.
Pronto, você tem sua nova lista de imagens normalmente.
Eu não tenho problemas com modelagem de banco, afinal nunca usei "de fato" essa solução das imagens que falei, apenas foi o que me veio a cabeça, disse o "faço" para o caso atual em que está sendo discutido, mas ... é questão de analisar a situação.
Com certeza a tabela imagens sendo separada fica muito fácil Atualizar a referencia, remover ... e adicionar, enfim.
Já tive casos em que a situação tem que ser resolvida diretamente no banco, ou seja sem alterar no PHP.
Se quiser, te mostro uma modelagem minha que toda a parte matemática do sistema fica acoplado no SQL, da mesma forma em que existem casos que você cria uma tabela e guarda as imagens e casos que você pensa em outra solução.
>
E Evandro, seu teste tá meio errado aí não ? eu não falei sobre tipos de campo, e sim sobre o conteúdo no banco que no final das contas resume-se a exibição dele (de qualquer forma referência ou não, vai cair na tag img).
Procurei não fugir do objetivo inicial do tópico. Guardar a imagem ou o caminho no BD. Se for pra usar a tag IMG não precisaria nem dessa papagaiada toda. Armazena o caminho físico mesmo por todas as desvantagens do base64 já citadas nesse tópico.
Para guardar a imagem, usando Postgres, temos duas formas: Long Object ou encoded.
Para guardar o caminho, eu só conheço uma forma.
Conhecidas todas as possibilidades, criei os testes e isolei as execuções para ter um benchmark mais preciso possível.
Analisando as imagens postadas, vi que havia algo muito errado. Então reiniciei o computador em modo terminal, sem X, sem acesso à Internet e executei os testes 5 vezes
$ php -d xdebug.profiler_enable=1 /usr/local/bin/phpunit --repeat 5 PsqlTest.php
O cachegrind gerado pode ser obtido aqui
Aqui as informações relevantes e a minha dedução sobre a performance:
/applications/core/interface/imageproxy/imageproxy.php?img=http://files.myopera.com/EvandroOliveira/albums/11602162/new-benchmark.jpg&key=728bbb70f16109fc988cf89a0402700ad0c64bbf6c7c17cf83c73ee4654e2c5e" alt="new-benchmark.jpg" />
Usar o tipo Long Object é o menos performático. Mas se a opção existe, deve ter suas funções, talvez com imagens maiores possa ser mais rápida que codificar em base64.
Fato curioso é que a exclusão da imagem em base64 é sempre mais rápida. Talvez o Postgres apenas remova a referência ao campo TEXT ao invés de truncá-lo, como faz com varchar.
Para inserção e consulta, armazenar apenas o caminho físico e abrí-lo externamente se mostrou o menos latente. Por motivos óbvios.
Ao contrário da sequência que eu imaginava, armazenar um conteúdo binário no Postgres envolve um passo a mais ao invés de um passo a menos - lo_export e lo_import. Como eu disse, deve ter sua utilidade.
Como esperado, base64 não parece ser uma solução muito inteligente por criar a mesma camada de necessidade do tipo Long Object (lo_export -> base64_decode, lo_import -> base64_encode).
Excetuando-se pela exclusão, usar apenas o caminho físico do arquivo é mais rápido em todos os cenários. Usei a função copy para simular a funcionalidade de move_uploaded_file e ter um resultado mais próximo possível do real.
Sobre o acesso das informações do produto, veja bem, você sabe o ID do produto, mas não sabe quantas fotos são, ali eu e você afirmamos que são 5, mas e se não forem ? de qualquer forma você vai consultar o banco pra pegar as informações, o que eu faço nesse caso de várias imagens, é simplesmente uma coluna TEXT que vai ficar os paths delimitados por ';', na hora eu apenas quebro a string e exibo.
hehehe, sabia que você ia dizer "glob", mas aí você não contradisse o que falou acima sobre consultar o diretório atrás dos path's das imagens ... ? lá no seu exemplo do corredor, se eu entendi direito.
Entretanto, os testes apesar de não terem exatamente o foco do tópico, provam e respondem outras perguntas sobre performance no armazenamento de dados nesses tipos de campo, como eu não conheço muito mongodb, teria algum tipo de campo em específico para esse tipo de armazenamento ?
hehehe, sabia que você ia dizer "glob", mas aí você não contradisse o que falou acima sobre consultar o diretório atrás dos path's das imagens ... ? lá no seu exemplo do corredor, se eu entendi direito.
loja.com/?produto=1
$fotos = glob($_GET['produto'] . '-*.jpg'); // array('1-capa.jpg','1-conteudo.jpg','1-contracapa.jpg');
Sem banco de dados ;)
A questão do corredor é que o tópico trata sobre passar o conteúdo da imagem via data-uri. Para isso precisaríamos ler o conteúdo. Para ler o conteúdo, ou ele já está no banco, ou vamos buscar o caminho no banco. Era esse o problema. Agora não tratamos mais do conteúdo, não precisamos chegar até a imagem. A ida ao corredor de arquivos é apenas uma. Devolvemos no final do corredor o caminho. Esse corredor será percorrido novamente, mas por uma nova requisição HTTP.
>
Entretanto, os testes apesar de não terem exatamente o foco do tópico, provam e respondem outras perguntas sobre performance no armazenamento de dados nesses tipos de campo, como eu não conheço muito mongodb, teria algum tipo de campo em específico para esse tipo de armazenamento ?
Pera ae, tá uma confusão danada isso aqui... está até interessante o debate ae, hehe, mas deixa só eu confirmar se entenderam exatamente o que eu pretendia fazer: era simples, apenas armazenar num campo de texto qualquer do banco de dados, o código base64, para usar como data-uri numa tag <img.... />.
Não entendi muito bem, mas parece que estão falando ae de criar o base64 da imagem pelo SQL... eu pretendia usar a função base64_encode() para criar o código, e esse seria armazenado normalmente no banco de dados, como qualquer texto comum...
Exemplo de tabela que iria usar [images]:
+---+----------+-------------------------------------------------+
| id | formato | base64 |
+---+----------+-------------------------------------------------+
| 1 | gif/jpg/png | código-base64-da-imagem-aqui |
+---+----------+-------------------------------------------------+
Aí criaria uma função assim:
function getimg($id) {
$select = mysqli_query($conexao, "SELECT formato,base64 FROM images WHERE id='".$id."'");
$get = mysqli_fetch_array($select, MYSQLI_ASSOC);
return array($get['formato'], $get['base64']);
}
Usaria assim:
<img src="data:image/<?php echo getimg(1)[0]; ?>;base64,<?php echo getimg(1)[1]; ?>" />
Apenas isto. E quando eu fosse salvar a imagem no db, codificaria ela com a função base64_encode() do PHP e armazenaria o resultado no campo "base64" da tabela.
Foi isso que entenderam?
Obs: já não pretendo mais usar esse método pelo tanto de desvantagens que me apontaram (33% a mais de uso do disco, query lentas...), mas a discussão ae tá boa, não conheço muita coisa de banco de dados, só o básico, então está sendo bem interessante :)
>
Pera ae, tá uma confusão danada isso aqui... está até interessante o debate ae, hehe, mas deixa só eu confirmar se entenderam exatamente o que eu pretendia fazer: era simples, apenas armazenar num campo de texto qualquer do banco de dados, o código base64, para usar como data-uri numa tag <img.... />.
Não entendi muito bem, mas parece que estão falando ae de criar o base64 da imagem pelo SQL... eu pretendia usar a função base64_encode() para criar o código, e esse seria armazenado normalmente no banco de dados, como qualquer texto comum...
Exemplo de tabela que iria usar [images]:
+---+----------+-------------------------------------------------+
| id | formato | base64 |
+---+----------+-------------------------------------------------+
| 1 | gif/jpg/png | código-base64-da-imagem-aqui |
+---+----------+-------------------------------------------------+
Aí criaria uma função assim:
function getimg($id) {
$select = mysqli_query($conexao, "SELECT formato,base64 FROM images WHERE id='".$id."'");
$get = mysqli_fetch_array($select, MYSQLI_ASSOC);
return array($get['formato'], $get['base64']);
}
Usaria assim:
<img src="data:image/<?php echo getimg(1)[0]; ?>;base64,<?php echo getimg(1)[1]; ?>" />
Apenas isto. E quando eu fosse salvar a imagem no db, codificaria ela com a função base64_encode() do PHP e armazenaria o resultado no campo "base64" da tabela.
Foi isso que entenderam?
Obs: já não pretendo mais usar esse método pelo tanto de desvantagens que me apontaram (33% a mais de uso do disco, query lentas...), mas a discussão ae tá boa, não conheço muita coisa de banco de dados, só o básico, então está sendo bem interessante :)
Sim, entendemos perfeitamente. A minha opinião se resume a esse parágrafo:
>
Escrever as imagens como data-uri está fora de cogitação. Nunca sacrificaria as várias formas de caching e compressão nesse cenário.
data-uri pra mim seria uma saída pra um fundo degradê, por exemplo. Não imagens complexas. Às vezes o base64 gerado consegue ser maior que a imagem! Sem contar que eu teria que embutir isso no markup, atrasando a exibição do conteúdo textual.
Acredito que odebate se "acalorou" porque armazenar a imagem encoded no banco é similar a armazenar o conteúdo dela. Para isso temos outras formas.
O equivalente, seria você armazenar a imagem como um blob e, depois do select, imprimir o base64_encode dela.
Mas dá na mesma, todas as desvantagens já citadas.
Ah, ok...
Mas segundo o que Andrey me falou por private, nem pra elementos do layout, como o background que você citou, vale a pena usar o data-uri :(
O css vai ficar muito grande, demorar de carregar, e fazer uma bagunça.
Sendo assim, até hoje não vi utilidade do uso de imagens por esse modo, hehe
Vlw a todo ae;
Dúvida resolvida -
Taí... uma maneira de armazenar as imagens no FS sem precisar ter os caminhos para elas armazenados no banco de dados seria simplesmente coloca-las numa pasta cujo nome identifique o produto e usar glob para pegá-las.
envie o nome da imagem para o banco de dados.
exemplo de um script que eu tenho aqui:
function trocar_acentos ($imagem)
{
precisa ser aprimorado, mas ja da pra começar :)
qualquer modificação poste ai :D