Prefácio
Meu aplicativo da web está armazenando dados no
localStorage
. Isso foi conveniente até que eu quisesse que o usuário visse a mesma coisa ao acessar o site a partir de dispositivos diferentes. Ou seja, era necessário armazenamento remoto.
Mas o aplicativo está "hospedado" nas páginas do GitHub e não possui uma parte do servidor. Decidi não criar um servidor, mas armazenar os dados com terceiros. Isso oferece vantagens significativas:
- Não há necessidade de pagar pelo servidor, isso não prejudica sua estabilidade e disponibilidade.
- Menos código, menos erros.
- O usuário não precisa se registrar no meu aplicativo (isso é irritante para muitos).
- A privacidade é maior e o usuário sabe que seus dados são armazenados em um local em que ele provavelmente confia mais que eu.
Primeiro, a escolha caiu no
remoteStorage.js . Eles oferecem um protocolo aberto de troca de dados, uma API bastante agradável, a capacidade de integrar-se ao Google Drive e Dropbox, além de seus servidores. Mas esse caminho acabou sendo um beco sem saída (por que - uma história separada).
No final, decidi usar o Google Drive diretamente e a
Biblioteca do cliente da API do
Google (doravante GAPI) como uma biblioteca para acessá-lo.
Infelizmente, a documentação do Google é decepcionante e a biblioteca GAPI parece inacabada, além disso, possui várias versões e nem sempre é claro qual delas está em questão. Portanto, a solução para meus problemas teve que ser coletada em partes da documentação, perguntas e respostas no StackOverflow e postagens aleatórias na Internet.
Espero que este artigo poupe tempo se você decidir usar o Google Drive em seu aplicativo.
Preparação
A seguir, é apresentada uma descrição de como obter chaves para trabalhar com a API do Google. Se você não estiver interessado, vá direto para a próxima parte.
Recebendo chavesNo Google Developer Console,
crie um novo projeto, digite um nome.
No "Painel de controle", clique em "Ativar API e serviços" e ative o Google Drive.
Em seguida, vá para a seção API e serviços -> Credenciais, clique em "Criar credenciais". Há três coisas que você precisa fazer:
- Configure "Janela OAuth Access Request". Digite o nome do aplicativo, seu domínio na seção "Domínios autorizados" e um link para a página principal do aplicativo. Outros campos são opcionais.
- Na seção "Credenciais", clique em "Criar credenciais" -> "Identificador de cliente OAuth". Selecione o tipo "Aplicativo da Web". Na janela de configurações, adicione "Fontes de Javascript permitidas" e "URIs de redirecionamento permitido":
- Seu domínio (obrigatório)
http://localhost:8000
(opcional para trabalhar localmente).

- Na seção "Credenciais", clique em "Criar credenciais" -> "Chave da API". Nas configurações de chave, especifique as restrições:
- Tipo de aplicativo permitido -> referenciadores HTTP (sites)
- Aceite solicitações http das seguintes fontes de referência (sites) -> seu domínio e host local (como no ponto 2).
- APIs válidas -> API do Google Drive

A seção Credenciais deve se parecer com isso:

Aqui terminamos. Passamos para o código.
Inicialização e login
A maneira recomendada pelo Google de ativar o GAPI é colar o seguinte código no seu HTML:
<script src="https://apis.google.com/js/api.js" onload="this.onload=function(){}; gapi.load('client:auth2', initClient)" onreadystatechange="if (this.readyState === 'complete') this.onload()"> </script>
Depois de carregar a biblioteca, a função
initClient
será chamada, a qual devemos escrever por nós mesmos. Sua aparência típica é a seguinte:
function initClient() { gapi.client.init({
Para armazenamento de dados, usaremos a chamada
pasta Application Data . Suas vantagens sobre uma pasta comum:
- O usuário não o vê diretamente: os arquivos dele não obstruem seu espaço pessoal e não podem arruinar nossos dados.
- Outras aplicações não o veem e também não podem estragá-lo.
- O escopo, mencionado acima, concede ao aplicativo acesso a ele, mas não dá acesso ao restante dos arquivos do usuário. Ou seja, não assustaremos uma pessoa com pedidos de acesso aos seus dados pessoais.
Após a inicialização bem-sucedida da API do Google, a função faz o seguinte:
- Começa a capturar eventos de logon / logout - provavelmente, isso sempre deve ser feito.
- Inicializa o aplicativo. Isso pode ser feito antes de carregar e inicializar o GAPI - como você preferir. Meu procedimento de inicialização foi um pouco diferente se o Google não estiver disponível. Alguém pode dizer que isso não acontece :) Mas, em primeiro lugar, você pode ser inteligente com chaves e direitos de acesso no futuro. Em segundo lugar, por exemplo, na China, o Google é proibido.
O login e o logout são feitos simplesmente:
function isGapiLoaded() { return gapi && gapi.auth2 } function logIn() { if (isGapiLoaded()) {
Você receberá resultados de login no manipulador
onSignIn
:
function isLoggedIn() { return isGapiLoaded() && gapi.auth2.getAuthInstance().isSignedIn.get() } function onSignIn() { if (isLoggedIn()) {
Infelizmente, trabalhar com arquivos não é tão óbvio.
Promise helper
O GAPI não retorna promessas normais. Em vez disso, é usada sua própria interface Thennable, que é semelhante às promessas, mas não exatamente. Portanto, para a conveniência do trabalho (principalmente para usar
async/await
), faremos um pequeno ajudante:
function prom(gapiCall, argObj) { return new Promise((resolve, reject) => { gapiCall(argObj).then(resp => { if (resp && (resp.status < 200 || resp.status > 299)) { console.log('GAPI call returned bad status', resp) reject(resp) } else { resolve(resp) } }, err => { console.log('GAPI call failed', err) reject(err) }) }) }
Essa função leva o método GAPI e os parâmetros para ele como o primeiro argumento e retorna Promise. Então você verá como usá-lo.
Trabalhar com arquivos
Lembre-se sempre de que
o nome do arquivo no Google Drive não é exclusivo . Você pode criar qualquer número de arquivos e pastas com o mesmo nome. Somente o identificador é único.
Para tarefas básicas, você não precisa trabalhar com pastas; portanto, todas as funções abaixo funcionam com arquivos na raiz da pasta Dados do Aplicativo. Os comentários indicam o que precisa ser alterado para funcionar com pastas. A documentação do Google está aqui .
Crie um arquivo vazio
async function createEmptyFile(name, mimeType) { const resp = await prom(gapi.client.drive.files.create, { resource: { name: name,
Essa função assíncrona cria um arquivo vazio e retorna seu identificador (string). Se esse arquivo já existir, um novo arquivo com o mesmo nome será criado e seu ID será retornado. Se você não quiser isso, verifique primeiro se não há arquivos com o mesmo nome (veja abaixo).
O Google Drive não é um banco de dados completo. Por exemplo, se você deseja que vários usuários trabalhem da mesma conta do Google simultaneamente a partir de dispositivos diferentes, pode haver problemas com a resolução de conflitos devido à falta de transações. Para essas tarefas, é melhor não usar o Google Drive.
Trabalhar com o conteúdo do arquivo
O GAPI (para JavaScript baseado em navegador) não fornece métodos para trabalhar com o conteúdo dos arquivos (muito estranho, não é?). Em vez disso, existe um método de
request
geral (um invólucro fino sobre uma solicitação AJAX simples).
Por tentativa e erro, cheguei às seguintes implementações:
async function upload(fileId, content) {
Pesquisa de arquivo
async function find(query) { let ret = [] let token do { const resp = await prom(gapi.client.drive.files.list, {
Essa função, se você não especificar a
query
, retornará todos os arquivos na pasta do aplicativo (uma matriz de objetos com campos de
id
e
name
), classificados por hora de criação.
Se você especificar a string de
query
(a sintaxe é descrita
aqui ), ela retornará apenas os arquivos que correspondem à consulta. Por exemplo, para verificar se
config.json
um arquivo chamado
config.json
, é necessário fazer
if ((await find('name = "config.json"')).length > 0) {
Excluindo arquivos
async function deleteFile(fileId) { try { await prom(gapi.client.drive.files.delete, { fileId: fileId }) return true } catch (err) { if (err.status === 404) { return false } throw err } }
Essa função exclui o arquivo por ID e retorna
true
se ele foi excluído com êxito e
false
se não houver esse arquivo.
Sincronizar
É recomendável que o programa funcione principalmente com o
localStorage
, e o Google Drive seja usado apenas para sincronizar dados do
localStorage
.
A seguir, é apresentada uma estratégia simples de sincronização da configuração:
- A nova configuração é baixada do Google Drive com um login e, a cada 3 minutos, substituindo a cópia local;
- As alterações locais são lançadas no Google Drive, substituindo o que estava lá;
- o
fileID
configuração é armazenado em cache no localStorage
para acelerar o trabalho e reduzir o número de solicitações; - As situações tratadas corretamente (incorretamente) são quando o Google Drive possui vários arquivos de configuração e quando alguém exclui ou arruina nosso arquivo de configuração.
- Os detalhes da sincronização não afetam o restante do código do aplicativo. Para trabalhar com a configuração, você usa apenas duas funções:
getConfig()
e saveConfig(newConfig)
.
Em um aplicativo real, você provavelmente deseja implementar uma manipulação de conflitos mais flexível ao carregar / descarregar uma configuração.
Conclusão
Parece-me que o armazenamento de dados de um site no Google Drive é ótimo para pequenos projetos e protótipos. Não é apenas fácil de implementar e apoiar, mas também ajuda a reduzir o número de entidades desnecessárias no universo. E espero que meu artigo o ajude a economizar tempo se você escolher esse caminho.
PS O código do projeto real
está no GitHub ,
você pode experimentá-lo
aqui .