[Node.js] Manipulação de arquivos
![[Node.js] Manipulação de arquivos](/_next/image?url=https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fstock%2Funsplash%2FlRoX0shwjUQ%2Fupload%2Fcc3d6f404188dc5225f1757ee4d76628.jpeg&w=3840&q=75)
Problema
Subimos vários videos em formato de streaming (HLS) para a AWS, mas eles ficaram nomeados em UTF-8, usando caracteres como ç á ã â. O problema é que esses caracteres especiais não são aceitos como parte de uma URI, então não conseguimos abrir os vídeos por browsers que não fazer essa conversão automaticamente (ex. Safari).
Por isso preciso fazer um script que itere sobre um arquivo, leia cada linha, converta em um formato seguro para URI e escreva em um outro arquivo de output.
O fluxo é basicamente o seguinte:
Pegar arquivo m3u8 do video na AWS
- Fazer um backup
Percorrer conteudo e converter caracteres especiais para formato URI friendly
Ignorar linhas começadas em #
Salvar os nomes para não precisar percorrer denovo
Ler nomes dos sub-arquivos
Repetir passo (1) para cada um até chegar no final (arquivos .ts)
- Provavelmente usar recursividade
Pegar o próximo arquivo na AWS e repetir o processo
Fazer o Upload do arquivo atualizado para a AWS
Desenvolvimento
Será usado Node.js v18 para escrever o script, que é a linguagem que eu tenho mais familiaridade, além do SDK da AWS para fazer a conexão com o bucket S3 onde está os arquivos.
Solução
A manipulação de arquivos pelo Node.js foi feita através do módulo FileSystem (fs). O FileSystem também possui uma parte de promises com varias outras funções que funcionam com a arquitetura async await .
Esta foi a função para o processamento de arquivos. Não vou tocar na parte sobre a AWS aqui, pois não é o escopo.
const { open } = require('fs/promises');
async function processarHLS(path, outputPath) {
let file;
let out;
let stream;
let subpaths = [];
try {
/* (1) */
file = await open(path, 'r+');
out = await open(outputPath, 'w+');
stream = out.createWriteStream();
/* (2) */
for await (let line of file.readLines()) {
/* (3) */
stream.cork();
if (!line.startsWith('#')) {
subpaths.push(line);
line = line
.split('')
.map((c) => {
if (/[A-z0-9%\-_]/.test(c)) return c;
return encodeURI(c);
})
.join('');
}
stream.write(line + '\n');
/* (4) */
process.nextTick(() => {
stream.uncork();
});
}
} catch (error) {
console.error(error);
return false;
} finally {
stream?.close();
out?.close();
file?.close();
}
return subpaths;
}
O que está acontecendo nesse código? Tem várias partes interessantes como o for await , a escrita e leitura simultânea de dois arquivos e a escrita com buffer através do stream.cork() e stream.uncork() .
Abertura dos arquivos de input e output em modo de leitura
r+e escritaw+. E criação de uma stream de escrita com a ajuda do FileSystem.Essa estrutura me surpreendeu, mas é simples.
for awaité usado com iteradores assíncronos, ofile.readLines()no caso, que a iteração dentre os elementos não é linear igual em um array, mas cada iteração em sí é assíncrona, como ir no arquivo e ler a próxima linha.Por padrão a escrita da stream não é bufferizada, cada
stream.write()gera um buffer específico para o arquivo. Usando ocorkeuncorké possível usar a estrutura de write e flush como em outras linguagens, o que torna o processo mais otimizado, sendo que, a partir da chamada dostream.cork(), cada buffer único dowriteé armazenado e escrito de uma vez só quandostream.uncork()é chamado. Também pode minimizar a chance de acessos simultâneos no arquivo.Não entendi bem, mas por recomendação da documentação do FileSystem, o
process.nextTické usado para postergar a execução dostream.cork()para o próximo tick.

![[CSS] Footer no final da página](/_next/image?url=https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1685140125751%2F9a0869f2-a6bf-4146-9dd7-a898ca486abe.png&w=3840&q=75)