Vários clientes nos abordaram e perguntaram como podem criar um aplicativo da web do tipo Google Docs usando nossas APIs. Google Docs é um processador de texto que permite aos usuários criar e editar arquivos on-line enquanto colaboram com outros usuários em tempo real.

Esta postagem do blog explica como é fácil criar uma versão lite do Google Docs com os seguintes recursos:

  • Edição de Rich Text (alterar a fonte do texto, tamanho, cor, estilo (negrito, itálico), alinhamento etc.).
  • Edição colaborativa em tempo real do mesmo documento. Vários usuários podem acessar o documento ao mesmo tempo e modificá-lo.
  • Carregue o conteúdo de um documento do Word existente em um editor.
  • Salve o texto no editor como documento MS Word, PDF, TXT ou HTML.

Nosso produto final terá a seguinte aparência:

Google Docs como a interface do aplicativo

Ferramentas e tecnologias – Crie Google Docs como App

Vamos desenvolver o Google Docs como aplicativo web em ASP.NET Core e usar as duas bibliotecas a seguir:

  • Firepad is an open-source, collaborative text editor. It uses the Firebase Realtime Database as a backend so it requires no server-side code and can be added to any web app simply by including the JavaScript files.
  • GroupDocs.Editor para .NET gives us an ability to edit most popular document formats using any WYSIWYG editor without any additional applications. We will load document via GroupDocs.Editor into Firepad, edit document in a way we want and save it back to original document format.

Eu usei o Visual Studio para Mac como um IDE. No entanto, você pode baixar a edição gratuita da comunidade do Visual Studio, dependendo da sua plataforma, aqui. Vamos começar.

Crie um novo projeto de aplicativo Web ASP.NET Core e nomeie o projeto como “GoogleDocsLite”.

Criar um novo aplicativo Web ASP.NET Core

Execute o aplicativo para garantir que tudo esteja configurado corretamente.

Integrar Firepad

Podemos adicionar o Firepad ao nosso aplicativo da web incluindo os seguintes arquivos JavaScript no arquivo seção de Layout.cshtml.

<!-- Firebase -->
<script src="https://www.gstatic.com/firebasejs/7.13.2/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/7.13.2/firebase-auth.js"></script>
<script src="https://www.gstatic.com/firebasejs/7.13.2/firebase-database.js"></script>

<!-- CodeMirror -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.17.0/codemirror.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.17.0/codemirror.css" />

<!-- Firepad -->
<link rel="stylesheet" href="https://firepad.io/releases/v1.5.9/firepad.css" />
<script src="https://firepad.io/releases/v1.5.9/firepad.min.js"></script>

<!-- userlist -->
<script src="~/js/firepad-userlist.js"></script>
<link rel="stylesheet" href="~/css/firepad-userlist.css" />

Para criar um Firepad, inicializamos o Firebase, o CodeMirror e depois o Firepad. Adicione o seguinte script e código HTML em Index.cshtml.

<script>
    function init() {
        // Initialize Firebase.
        // TODO: replace with your Firebase project configuration.
        var config = {
            apiKey: '',
            authDomain: "",
            databaseURL: ""
        };
        firebase.initializeApp(config);
        
        // Get Firebase Database reference.
        var firepadRef = getExampleRef();
        
        // Create CodeMirror (with lineWrapping on).
        var codeMirror = CodeMirror(document.getElementById('firepad'), { lineWrapping: true });

        // Create a random ID to use as our user ID (we must give this to firepad and FirepadUserList).
        var userId = Math.floor(Math.random() * 9999999999).toString();

        // Create Firepad (with rich text features and our desired userId).
        var firepad = Firepad.fromCodeMirror(firepadRef, codeMirror,
                    { richTextToolbar: true, richTextShortcuts: true, userId: userId });

        // Create FirepadUserList (with our desired userId).
        var firepadUserList = FirepadUserList.fromDiv(firepadRef.child('users'),
        document.getElementById('userlist'), userId);
    }
    
    // Helper to get hash from end of URL or generate a random one.
    function getExampleRef() {
        var ref = firebase.database().ref();
        var hash = window.location.hash.replace(/#/g, '');
        if (hash) {
            ref = ref.child(hash);
        } else {
            ref = ref.push(); // generate unique location.
            window.location = window.location + '#' + ref.key; // add it as a hash to the URL.
        }
        if (typeof console !== 'undefined') {
            console.log('Firebase data: ', ref.toString());
        }
        return ref;
    }
</script>

<div id="userlist"></div>
<div id="firepad"></div>

Substitua o conteúdo da configuração pela configuração do seu próprio projeto do Firebase.

Queremos que o script acima seja executado assim que uma página da Web tiver carregado completamente todo o conteúdo (arquivos de script, arquivos CSS etc.). Então, chame a função init() do atributo de evento onLoad de elemento em Layout.cshtml.

<body onload="init()">

Seu <body> elemento deve ter a seguinte aparência. Se contiver tags desnecessárias como <header> , <footer> , por favor, remova-os.

Elemento do corpo

Se você executar o projeto, notará que o firepad e a lista de usuários não estão alinhados corretamente. Use o seguinte código CSS para ajustar o tamanho/posição do firepad e da lista de usuários. Você pode adicionar o seguinte código dentro elemento de Layout.cshtml.

<style>
    html {
        height: 100%;
    }

    body {
        margin: 0;
        height: 100%;
    }

    /* We make the user list 175px and firepad fill the rest of the page. */
    #userlist {
        position: absolute;
        left: 0;
        top: 50px;
        bottom: 0;
        height: auto;
        width: 175px;
    }

    #firepad {
        position: absolute;
        left: 175px;
        top: 50px;
        bottom: 0;
        right: 0;
        height: auto;
    }
</style>

O Firepad foi configurado com sucesso.

Carregar o conteúdo de um documento do Word existente em um editor

Agora queremos dar aos nossos usuários uma maneira de carregar o conteúdo de um documento do Word existente no editor de texto. No frontend, adicionamos um elemento do tipo arquivo que permite ao usuário selecionar um documento do Word de sua máquina local. No backend, usamos a biblioteca GroupDocs.Editor para recuperar o conteúdo de um documento do Word como string HTML. Por fim, usamos o método setHtml() do Firepad para mostrar o conteúdo no editor de texto.

Adicione o seguinte elemento no arquivo Index.cshtml antes marcação.

<form method="post" enctype="multipart/form-data" id="uploadForm">
    <input asp-for="UploadedDocument" />
    <input type="submit" value="Upload Document" class="btn btn-primary" asp-page-handler="UploadDocument" />
</form>

No arquivo Index.cshtml.cs, defina uma propriedade correspondente.

[BindProperty]
public IFormFile UploadedDocument { get;  set; }

Execute o projeto e clique no botão Escolher arquivo. Selecione um documento do Word que você deseja carregar e clique no botão Carregar documento. Nada acontecerá porque ainda não definimos o manipulador em Index.cshtml.cs. Antes de fazermos isso, vamos primeiro adicionar a biblioteca GroupDocs.Editor em nosso projeto.

Integrar GroupDocs.Editor

O GroupDocs.Editor está disponível como um pacote NuGet para que possamos adicioná-lo facilmente ao nosso projeto. Clique com o botão direito do mouse no projeto e selecione a opção Gerenciar Pacotes NuGet. A janela Gerenciar Pacotes NuGet será aberta, selecione a guia Procurar e insira GroupDocs.Editor no campo de pesquisa. O GroupDocs.Editor deve aparecer como primeiro resultado, selecione-o e clique no botão Adicionar Pacote.

Adicionar GroupDocs.Editor por meio do Gerenciador de Pacotes NuGet

Quando o pacote for adicionado com êxito, ele aparecerá na subpasta NuGet na pasta Dependências.

Manipulação de dados de formulário

Agora escrevemos um manipulador (método OnPostUploadDocument()) que será chamado quando um usuário clicar no botão Upload Document. O objeto UploadedDocument (do tipo IFormFile) contém o conteúdo do documento carregado. Primeiro, salvamos o documento no servidor e, em seguida, usamos a biblioteca GroupDocs.Editor para obter seu conteúdo como string HTML. Adicione o seguinte código no arquivo Index.cshtml.cs.

private readonly IWebHostEnvironment _hostingEnvironment;

public string DocumentContent { get; set; }

public IndexModel(IWebHostEnvironment hostingEnvironment)
{
    _hostingEnvironment = hostingEnvironment;
}

public void OnPostUploadDocument()
{
    var projectRootPath = Path.Combine(_hostingEnvironment.ContentRootPath, "UploadedDocuments");
    var filePath = Path.Combine(projectRootPath, UploadedDocument.FileName);
    UploadedDocument.CopyTo(new FileStream(filePath, FileMode.Create));
    ShowDocumentContentInTextEditor(filePath);
}

private void ShowDocumentContentInTextEditor(string filePath)
{
    WordProcessingLoadOptions loadOptions = new WordProcessingLoadOptions();
    Editor editor = new Editor(filePath, delegate { return loadOptions; }); //passing path and load options (via delegate) to the constructor
    EditableDocument document = editor.Edit(new WordProcessingEditOptions()); //opening document for editing with format-specific edit options

    DocumentContent = document.GetContent();
}

O Firepad fornece dois eventos para escuta. Um deles está ‘pronto’, que é acionado assim que o Firepad recupera o conteúdo inicial do editor. Anexamos um callback a este tipo de evento e no callback, passamos a string DocumentContent como um argumento para o método setHtml() do objeto firepad. Por favor, adicione o seguinte código na função init() em Index.cshtml.

firepad.on('ready', function () {
    if (firepad.isHistoryEmpty()) {
        var documentContent = '@Model.DocumentContent';
        if (documentContent.length != 0) {   
            firepad.setHtml(htmlDecode(documentContent));
        } else {
            firepad.setText("Welcome to your own private pad! Share the URL above and collaborate with your friends.");
        }
    }
});

Você deve ter notado que passamos a string documentContent primeiro para o método htmlDecode() antes de passar para o método setHtml(). É para substituir entidades de caracteres como <, > por sinais (< e >). O método htmlDecode() tem a seguinte aparência.

function htmlDecode(input) {
    var e = document.createElement('div');
    e.innerHTML = input;
    return e.childNodes[0].nodeValue;
}

Execute o projeto, agora você poderá carregar o conteúdo de um documento do Word em um editor.

Na parte II deste post, expliquei como podemos estender nosso aplicativo para incluir os seguintes recursos:

  • Baixe o conteúdo do editor como documento MS Word, PDF, TXT ou HTML.
  • Compartilhe o URL com amigos para que eles possam editar o documento ao mesmo tempo.

Por favor confira.

O código fonte completo do projeto está disponível no GitHub.

Veja também