O upload de MP3 do Google Drive no Android pode apresentar falhas em aplicações web desenvolvidas com Node.js e Multer, mesmo quando o envio de arquivos armazenados diretamente no celular funciona normalmente. Uma alternativa é criar uma cópia temporária do arquivo no navegador, enviá-la por meio do FormData e liberar a referência após a conclusão. Neste tutorial, veremos como implementar essa abordagem, identificar possíveis erros e validar o recebimento no servidor.
Por que o upload de MP3 do Google Drive pode falhar no Android?
Em aplicações web, o upload de arquivos normalmente utiliza um campo HTML do tipo file. Quando o usuário seleciona um MP3 armazenado no próprio dispositivo, o navegador geralmente consegue acessar seu conteúdo e enviá-lo ao servidor.
Entretanto, quando o arquivo é selecionado diretamente do Google Drive, o processo pode ser diferente. O Android utiliza mecanismos de seleção de documentos que permitem acessar arquivos disponibilizados por aplicativos e serviços de armazenamento em nuvem.
Nesse cenário, o Google Drive pode precisar disponibilizar ou transferir o conteúdo do arquivo antes que o navegador consiga utilizá-lo.
Dependendo do navegador, da implementação do formulário e da disponibilidade do arquivo, o upload pode falhar antes mesmo de chegar ao servidor Node.js.
Entre as possíveis causas estão:
- Falha ao disponibilizar o conteúdo do arquivo selecionado no Google Drive.
- Problemas na leitura do arquivo pelo navegador.
- Validação incorreta do tipo MIME do MP3.
- Limites de tamanho configurados no Multer ou no servidor web.
- Problemas na construção ou no envio do FormData.
- Interrupções de conexão durante o upload.
Importante: a seleção de um arquivo do Google Drive não significa necessariamente que o problema esteja no Multer. É necessário identificar em qual etapa a falha acontece.
Como diagnosticar o problema antes de alterar o código
O primeiro teste consiste em verificar se o navegador consegue ler o arquivo selecionado.
Considere um formulário com um campo de upload identificado como arquivo.
<input
type="file"
id="arquivo"
name="arquivo"
accept=".mp3,audio/mpeg"
> |
Adicione temporariamente o seguinte JavaScript:
document.getElementById('arquivo') .addEventListener('change', async function () { const arquivo = this.files[0]; if (!arquivo) { return; } console.log('Nome:', arquivo.name); console.log('Tamanho:', arquivo.size); console.log('Tipo MIME:', arquivo.type); try { const conteudo = await arquivo.arrayBuffer(); console.log( 'Arquivo lido com sucesso:', conteudo.byteLength, 'bytes' ); } catch (erro) { console.error( 'Erro ao ler o arquivo:', erro ); } }); |
Execute esse teste no Android, selecionando primeiro um MP3 da pasta Downloads e depois um arquivo do Google Drive.
Se a leitura funcionar nas duas situações, mas o upload falhar apenas com o Drive, será necessário investigar a construção do FormData, o envio da requisição e o processamento no servidor.
Se o método arrayBuffer() apresentar erro, o problema ocorre antes de o Multer receber o arquivo.
Como funciona a solução com cópia temporária do MP3?
A solução consiste em criar um novo objeto File no navegador a partir do conteúdo do arquivo selecionado.
Esse novo arquivo será mantido temporariamente em memória e utilizado no envio ao servidor.
O processo segue estas etapas:
- O usuário seleciona um MP3 no Google Drive, Downloads ou outro local.
- O JavaScript obtém o conteúdo do arquivo selecionado.
- Uma cópia temporária é criada utilizando a API File.
- O arquivo temporário é adicionado ao FormData.
- O navegador envia o FormData para a aplicação Node.js.
- O Multer recebe e armazena o arquivo no servidor.
- Após a confirmação do envio, o JavaScript libera a referência à cópia temporária.
Essa abordagem permite padronizar a preparação do arquivo antes do upload, independentemente de sua origem.
Atenção: criar uma cópia temporária não garante a correção de todas as falhas relacionadas ao Google Drive. Se o navegador não conseguir acessar o conteúdo original, também não conseguirá criar a cópia.
Passo 1: criar o formulário HTML para upload de MP3
Crie um arquivo chamado index.html com um formulário para selecionar e enviar o MP3.
O formulário terá um campo para selecionar o arquivo, um botão para iniciar o upload e uma área para exibir o resultado da operação.
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta
name="viewport"
content="width=device-width, initial-scale=1"
>
<title>Upload de MP3</title>
</head>
<body>
<h2>Enviar arquivo MP3</h2>
<form id="formUpload">
<input
type="file"
id="arquivo"
name="arquivo"
accept=".mp3,audio/mpeg"
required
>
<button type="submit" id="btnEnviar">
Enviar MP3
</button>
</form>
<p id="mensagem" role="status"></p>
<script src="/upload.js"></script>
</body>
</html> |
O atributo accept orienta o seletor de arquivos a apresentar formatos compatíveis com MP3. Entretanto, ele não substitui a validação no servidor.
Passo 2: criar a cópia temporária do arquivo com JavaScript
Crie o arquivo upload.js no mesmo diretório do formulário.
O código abaixo lê o arquivo selecionado, cria uma cópia temporária e envia essa cópia utilizando Fetch e FormData.
const inputArquivo = document.getElementById('arquivo'); const formulario = document.getElementById('formUpload'); const botaoEnviar = document.getElementById('btnEnviar'); const mensagem = document.getElementById('mensagem'); let arquivoTemporario = null; let preparando = false; let enviando = false; let selecaoAtual = 0; inputArquivo.addEventListener('change', async function () { const selecao = ++selecaoAtual; arquivoTemporario = null; const arquivoOriginal = this.files[0]; if (!arquivoOriginal) { mensagem.textContent = ''; return; } preparando = true; botaoEnviar.disabled = true; mensagem.textContent = 'Preparando arquivo...'; try { if (!arquivoOriginal.name.toLowerCase().endsWith('.mp3')) { throw new Error('Selecione um arquivo MP3.'); } const conteudo = await arquivoOriginal.arrayBuffer(); if (selecao !== selecaoAtual) { return; } arquivoTemporario = new File( [conteudo], arquivoOriginal.name, { type: 'audio/mpeg', lastModified: Date.now() } ); mensagem.textContent = 'Arquivo pronto para envio.'; console.log({ nome: arquivoTemporario.name, tamanho: arquivoTemporario.size, tipo: arquivoTemporario.type }); } catch (erro) { if (selecao === selecaoAtual) { arquivoTemporario = null; mensagem.textContent = erro.message; console.error('Erro ao preparar MP3:', erro); } } finally { if (selecao === selecaoAtual) { preparando = false; botaoEnviar.disabled = enviando; } } }); formulario.addEventListener('submit', async function (evento) { evento.preventDefault(); if (preparando || enviando) { return; } if (!arquivoTemporario) { mensagem.textContent = 'Selecione um MP3 válido.'; return; } const formData = new FormData(); formData.append( 'arquivo', arquivoTemporario, arquivoTemporario.name ); enviando = true; botaoEnviar.disabled = true; inputArquivo.disabled = true; mensagem.textContent = 'Enviando arquivo...'; try { const resposta = await fetch('/upload', { method: 'POST', body: formData }); const resultado = await resposta.json(); if (!resposta.ok) { throw new Error( resultado.mensagem || 'Falha no upload.' ); } mensagem.textContent = 'MP3 enviado com sucesso!'; arquivoTemporario = null; selecaoAtual++; formulario.reset(); } catch (erro) { mensagem.textContent = erro.message; console.error('Erro no upload:', erro); } finally { enviando = false; inputArquivo.disabled = false; botaoEnviar.disabled = preparando; } }); |
Por que utilizar arrayBuffer()?
O método arrayBuffer() permite ler o conteúdo binário do arquivo selecionado.
Depois da leitura, o construtor File cria um novo objeto com os dados obtidos.
Isso permite preparar uma cópia independente para o envio, em vez de utilizar diretamente a referência original do campo de seleção.
Entretanto, o método carrega o conteúdo completo em memória. Em dispositivos Android com pouca memória disponível, arquivos grandes podem causar lentidão ou falhas.
Por esse motivo, a técnica deve ser utilizada considerando o tamanho máximo dos arquivos aceitos pela aplicação.
Por que não definir Content-Type manualmente no Fetch?
Ao enviar um FormData, o navegador configura automaticamente o cabeçalho multipart/form-data e o delimitador necessário para separar os campos da requisição.
Não configure manualmente esse cabeçalho no Fetch, pois a ausência ou inconsistência do delimitador pode impedir o Multer de interpretar corretamente o arquivo recebido.
Passo 3: configurar o Multer no Node.js
No backend, utilizaremos Express e Multer para receber o MP3 enviado pelo navegador.
Instale as dependências no projeto:
npm install express multer |
Crie o arquivo server.js:
const express = require('express'); const multer = require('multer'); const path = require('path'); const fs = require('fs'); const crypto = require('crypto'); const app = express(); const PORT = process.env.PORT || 3000; const pastaUploads = path.join(__dirname, 'uploads'); fs.mkdirSync(pastaUploads, { recursive: true }); const storage = multer.diskStorage({ destination: function (req, file, cb) { cb(null, pastaUploads); }, filename: function (req, file, cb) { const nomeSeguro = crypto.randomUUID() + '.mp3'; cb(null, nomeSeguro); } }); const upload = multer({ storage: storage, limits: { fileSize: 50 * 1024 * 1024, files: 1 }, fileFilter: function (req, file, cb) { const extensao = path.extname( file.originalname ).toLowerCase(); if (extensao !== '.mp3') { return cb( new Error('Apenas arquivos MP3 são permitidos.') ); } cb(null, true); } }); app.use(express.static( path.join(__dirname, 'public') )); app.post('/upload', function (req, res) { upload.single('arquivo')(req, res, function (erro) { if (erro) { if (erro instanceof multer.MulterError) { return res.status(400).json({ sucesso: false, mensagem: erro.message }); } return res.status(400).json({ sucesso: false, mensagem: erro.message }); } if (!req.file) { return res.status(400).json({ sucesso: false, mensagem: 'Nenhum arquivo recebido.' }); } res.json({ sucesso: true, mensagem: 'Upload realizado com sucesso.', arquivo: req.file.filename, tamanho: req.file.size }); }); }); app.listen(PORT, function () { console.log( 'Servidor iniciado na porta ' + PORT ); }); |
Nesse exemplo, o Multer utiliza armazenamento em disco e limita cada arquivo a 50 MB.
O nome do arquivo armazenado é gerado pelo servidor, evitando utilizar diretamente o nome enviado pelo usuário como caminho de gravação.
Observação de segurança: o exemplo valida a extensão para demonstrar o funcionamento do upload, mas essa verificação não confirma que o conteúdo seja realmente um MP3. Em produção, implemente também uma validação do formato real do áudio antes de disponibilizar ou processar o arquivo. Arquivos rejeitados após a gravação devem ser removidos do armazenamento.
Passo 4: organizar os arquivos da aplicação
Para executar o exemplo, organize o projeto da seguinte forma:
projeto-upload/ | |-- server.js |-- package.json | |-- public/ | |-- index.html | |-- upload.js | |-- uploads/ |
O diretório public contém os arquivos utilizados pelo navegador.
O diretório uploads armazena os arquivos recebidos pelo Multer e não deve ser exposto diretamente como conteúdo público sem controles adicionais.
Para iniciar o servidor:
node server.js |
Em um ambiente local, abra o navegador e acesse:
http://localhost:3000 |
Para testar pelo Android, utilize um endereço de rede acessível pelo celular, considerando as configurações de rede e firewall do ambiente.
Como validar o upload de MP3 do Google Drive no Android
Depois de implementar a solução, execute os seguintes testes:
- Abra a aplicação no navegador do Android.
- Selecione um MP3 armazenado na pasta Downloads.
- Verifique se a mensagem “Arquivo pronto para envio” aparece.
- Envie o arquivo e confirme o retorno de sucesso.
- Repita o procedimento selecionando um MP3 diretamente do Google Drive.
- Confira se o servidor recebeu o arquivo e se ele pode ser reproduzido corretamente.
Quando o upload funcionar, o servidor deverá retornar uma resposta JSON semelhante a esta:
{
"sucesso": true,
"mensagem": "Upload realizado com sucesso.",
"arquivo": "nome-gerado-pelo-servidor.mp3",
"tamanho": 5242880
} |
O nome e o tamanho apresentados acima são apenas exemplos. Os valores reais dependerão do arquivo enviado.
Também é importante verificar se a cópia temporária deixa de ser referenciada após o envio bem-sucedido.
No exemplo, isso acontece por meio da atribuição:
arquivoTemporario = null; formulario.reset(); |
Essa operação libera a referência mantida pela aplicação e limpa o formulário. A recuperação efetiva da memória é gerenciada automaticamente pelo navegador.
O arquivo original armazenado no Google Drive não é apagado.
Problemas comuns ao utilizar cópias temporárias no upload
O arquivo do Google Drive não pode ser lido
Se arquivo.arrayBuffer() apresentar erro, o navegador não conseguiu obter o conteúdo necessário para criar a cópia.
Nesse caso, teste o download do MP3 para o armazenamento local do Android e tente selecioná-lo novamente.
Se o arquivo local funcionar, investigue o comportamento do seletor de arquivos, do navegador e do aplicativo Google Drive.
O Multer retorna LIMIT_FILE_SIZE
Esse erro indica que o arquivo ultrapassou o limite configurado no Multer.
No exemplo apresentado, o limite é de 50 MB:
limits: { fileSize: 50 * 1024 * 1024 } |
Ajuste o valor somente quando necessário, considerando a capacidade do servidor e as regras da aplicação.
O arquivo é enviado, mas o servidor não o reconhece
Verifique se o nome utilizado no FormData corresponde ao campo esperado pelo Multer.
No frontend:
formData.append('arquivo', arquivoTemporario); |
No backend:
upload.single('arquivo') |
Os nomes devem ser iguais. Caso contrário, o Multer poderá rejeitar o campo enviado.
O arquivo possui um tipo MIME diferente
Arquivos MP3 normalmente utilizam o tipo MIME audio/mpeg, mas o valor informado pelo navegador pode variar conforme a origem do arquivo.
O exemplo atribui audio/mpeg à cópia criada, mas isso apenas define o metadado do objeto File. Não converte nem valida o conteúdo do áudio.
Para uma aplicação em produção, não utilize exclusivamente o MIME informado pelo cliente para decidir se o arquivo é seguro ou válido.
O upload funciona no computador, mas falha no Android
Compare os testes entre navegadores e verifique se o problema acontece somente com arquivos selecionados da nuvem.
Também confira os logs do Node.js e as respostas HTTP da requisição de upload.
Se nenhuma requisição chegar ao servidor, o problema provavelmente está na seleção, preparação ou transmissão inicial do arquivo.
Alternativa para arquivos MP3 grandes
O uso de arrayBuffer() pode consumir uma quantidade significativa de memória, especialmente quando o arquivo possui dezenas de megabytes.
Uma alternativa é construir um novo File utilizando o objeto original como fonte:
const arquivoOriginal = inputArquivo.files[0]; const copia = new File( [arquivoOriginal], arquivoOriginal.name, { type: arquivoOriginal.type || 'audio/mpeg' } ); const formData = new FormData(); formData.append('arquivo', copia); |
Essa abordagem evita a leitura explícita do arquivo inteiro para um ArrayBuffer no JavaScript.
Entretanto, o navegador ainda poderá precisar acessar ou materializar os dados durante o envio. Portanto, a alternativa não garante a correção de problemas de leitura de arquivos provenientes do Google Drive.
Para uploads grandes ou conexões instáveis, também pode ser interessante avaliar mecanismos de envio em partes e retomada de transferências.
Resumo prático
Falhas no upload de MP3 do Google Drive no Android podem ocorrer antes de o arquivo chegar ao Node.js, especialmente quando o navegador encontra dificuldades para obter o conteúdo disponibilizado pelo serviço de armazenamento.
Uma abordagem possível é criar uma cópia temporária com JavaScript, enviá-la utilizando FormData e deixar o Multer responsável pelo recebimento no backend.
Após a confirmação do upload, a aplicação pode liberar a referência ao arquivo temporário e limpar o formulário, sem modificar o MP3 original armazenado no Google Drive.
Antes de aplicar essa solução como correção definitiva, confirme que o navegador consegue ler o arquivo original. Também considere o consumo de memória, os limites de upload e a validação real do formato MP3 no servidor.