Ícone do site Linha de Comando

Upload de MP3 do Google Drive no Android com Node.js e Multer

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:

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.


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:

  1. O usuário seleciona um MP3 no Google Drive, Downloads ou outro local.
  2. O JavaScript obtém o conteúdo do arquivo selecionado.
  3. Uma cópia temporária é criada utilizando a API File.
  4. O arquivo temporário é adicionado ao FormData.
  5. O navegador envia o FormData para a aplicação Node.js.
  6. O Multer recebe e armazena o arquivo no servidor.
  7. 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.




    
    
    Upload de MP3



    

Enviar arquivo MP3

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:

  1. Abra a aplicação no navegador do Android.
  2. Selecione um MP3 armazenado na pasta Downloads.
  3. Verifique se a mensagem “Arquivo pronto para envio” aparece.
  4. Envie o arquivo e confirme o retorno de sucesso.
  5. Repita o procedimento selecionando um MP3 diretamente do Google Drive.
  6. 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.

Sair da versão mobile