Durante o desenvolvimento de uma aplicação desktop com Qt, surgiu a necessidade de salvar arquivos gerados pelo programa diretamente no banco de dados MySQL.
Inicialmente, tentei ler o arquivo para uma variável do tipo QByteArray e inserir em um campo do tipo mediumblob, porém os dados sempre apareciam como NULL na tabela. Também tentei utilizar QDataStream para serializar os dados, mas o programa travava ao lidar com arquivos grandes. Ao verificar as configurações, descobri que o parâmetro max_allowed_packet no arquivo my.ini estava limitado a 16M. Alterei para 40M e mudei o tipo do campo para longblob, mas os dados continuavam sendo inseridos como NULL.
Pesquisando uma solução, encontrei uma referência sobre a função load_file() do MySQL:
SELECT LOAD_FILE('C:/ProgramData/MySQL/MySQL Server 8.0/Uploads/pic/11.jpg') AS RESULT;
Ao tentar executar, o resultado era vazio. Verifiquei então o parâmetro secure_file_priv:
show variables like 'secure_file_priv';
O valor retornado era um caminho de diretório específico, o que significa que apenas arquivos daquele local podem ser importados ou exportados. Se o valor fosse NULL, a importação e exportação seriam proibidas. Se fosse vazio, não haveria restrições.
Como o valor era um diretório específico, tentei modificar o arquivo my.ini adicionando a linha secure_file_priv = na seção [mysqld]. Mesmo assim, o valor não mudou. Pesquisei que era necessário reiniciar o MySQL, tentei o comando systemctl restart mysql.service sem sucesso. Encontrei uma alternativa acessando os serviços do Windows através do menu de contexto do "Meu Computador", mas ao parar o serviço, não consegui mais iniciá-lo.
Após pesquisar uma solução, o processo equivalia a reinstalar o MySQL do zero, conforme os passos abaixo:
- No diretório de instalação do MySQL (C:\Program Files\MySQL\MySQL Server 8.0), criei uma pasta data no mesmo nível do diretório bin (caso não existisse).
- Executei o comando de inicialização no prompt de comando (como administrador):
mysqld –initialize
- Instalei o serviço MySQL:
mysqld --install mysql
- Iniciei o serviço:
net start mysql
- Ao conectar ao banco, a senha enicial aleatória foi gerada no arquivo com extensão .err dentro da pasta data:
mysql –u root -p
- Alterei a senha do root:
alter user 'root'@'localhost' identified by 'senha_nova';
- Atualizei as permissões:
flush privileges;
Após recuperar o banco de dados, o objetivo era deixar secure_file_priv vazio para remover as restrições. Porém, após diversas tentativas, não consegui alterar esse valor.
Testei então a abordagem com load data local infile:
SHOW GLOBAL VARIABLES LIKE 'local_infile';
O valor inicial era OFF, precisei habilitar:
SET GLOBAL local_infile = true;
Depois tentei executar:
load data local infile 'D:\11.txt' into table user;
Essa abordagem não funcionou pois é para importação de dados, não de arquivos binários.
Por fim, testei o código original novamente e funcionou corretamente. Após a reinstalação, o valor de max_allowed_packet estava maior, cerca de 60M, superior ao que havia configurado anteriormente (40M).
Verifiquei com o comando:
show global variables like 'max_allowed%';
O problema foi resolvido, aparentemente pela reinstallação do MySQL.
Código Qt para inserção de arquivo:
void GerenciadorDados::salvarArquivo(QString nomeArquivo, QString caminho, QString categoria)
{
QFile arquivo(caminho);
if (!arquivo.open(QIODevice::ReadOnly))
return;
QByteArray dadosBinarios = arquivo.readAll();
arquivo.close();
QSqlQuery queryInserir;
queryInserir.prepare("INSERT INTO `arquivos-tabela`(nome, categoria, conteudo, dataCriacao) VALUES(:nome, :categoria, :conteudo, :data);");
queryInserir.bindValue(":nome", nomeArquivo);
queryInserir.bindValue(":categoria", categoria);
queryInserir.bindValue(":conteudo", dadosBinarios);
queryInserir.bindValue(":data", QDateTime::currentDateTime());
bool resultado = queryInserir.exec();
if (!resultado)
{
qDebug() << queryInserir.lastError().text();
}
}
Código para recuperação do arquivo:
void GerenciadorDados::recuperarArquivo()
{
QSqlQuery query;
bool ok = query.exec("select * from `arquivos-tabela`;");
QByteArray dadosRecuperados;
query.next();
dadosRecuperados = query.value("conteudo").toByteArray();
qDebug() << query.lastError().text();
QFile arquivoSaida("D:\\Dados\\output.tkb");
if (!arquivoSaida.open(QIODevice::WriteOnly))
return;
arquivoSaida.write(dadosRecuperados);
arquivoSaida.close();
}
A recuperação funciona perfeitamante. Inicialmente havia utilizado QDataStream, que adicionava bytes extras no início do arquivo. Utilizando a escrita direta, o processo é mais rápido e sem dados corrompidos.