구글 사진 대체품 만들기

이 모든 것은 내 친구가 나에게 인터넷 앱을 달라고 하는 데서 비롯되었다.봐라, 그는 우리의 모든 사진이 자동으로 어떤 클라우드 메모리 프로그램에 업로드되어 암호화되지 않았다는 사실에 놀랐다.따라서, 우리의 모든 개인 시간은 회사에서 그들의 ML 모델을 교육할 수 있다.그래서 그는 암호화 사진 저장 프로그램을 만드는 데 착수했다.그가 나에게 접근했을 때, 그는 이미 로컬 iOS와 안드로이드 프로그램을 가지고 있었다.따라서 모든 API 및 백엔드 시스템이 이미 설치되어 있습니다.심지어 정상적으로 작동하는 본체 프로그램조차도 그곳에서 놀 수 있다.나는 이 생각을 좋아하고 그를 돕는 것에 동의한다.6년 이상의 개발 경험을 통해 나는 프로젝트의 규모를 심각하게 과소평가했다.😝

내가 그것을 개발하기 시작했을 때, 나는 이것이 간단한 웹 응용 프로그램이 아니라는 것을 깨달았다. 당신은 API를 호출해서 사용자에게 예쁜 그림을 보여주고 하루의 일을 끝낼 수 있다.이것은 끝까지 암호화된 프로그램이기 때문에, 나는 모든 내용을 다운로드한 후에 복호화해서 사용자에게 보여 주어야 한다.blob/object 저장소에 의존해서 그림의 크기를 조절할 수 없습니다.이 모든 것은 속도에 영향을 주지 않는 상황에서 클라이언트에서 완성해야 한다.더 큰 문제는 동영상도 MVP의 일부다!😓

도전하다😪


응용 프로그램은 대부분의 문제를 해결했고 웹을 다시 실현하기만 하면 된다.그러나 웹 응용 프로그램이 파일 시스템에 접근할 수 없기 때문에 (만약 실험적인 API를 사용하지 않는다면) 사진과 동영상의 암호화/복호화는 번거로운 과정이기 때문에, 나는 모든 도구를 사용하여 성능을 확보해야 한다.
일찍이 나는 모든 암호화와 복호화 작업을 인터넷 종사자에게 맡겼다.따라서 메인 라인은 응용 프로그램에서 가장 무거운 부분이 없다.또한 파일 암호화/복호화에 필요한 시간도 줄어듭니다.나는 또한 Comlink를 사용하여 인터넷 종사자와 통신한다.처음에 우리는 AES 암호화를 사용했지만 나중에 바꾸었다libsodium.코드는 매우 간단하다.
우선, 우리는 워크맨을 만듭니다. 아래와 같습니다.
// Worker File
import * as Comlink from 'comlink';

export class Crypto {
    async encrypt(data, key) {
        // Encryption Logic
    }

    async decrypt(data, nonce, key) {
        // Decryption Logic
    }
}

Comlink.expose(Crypto);
그런 다음 Comlink를 사용하여 워크맨을 로드하고 인스턴스화하기만 하면 됩니다.
// Application File where the worker is used.
import * as Comlink from 'comlink';

const CryptoWorker = Comlink.wrap(
    new Worker('worker.js', { type: 'module' })
);
const worker = await new CryptoWorker();

await worker.decrypt(data, nonce, key);
그런 다음 UI에 로드된 축소판 그림을 CacheStorage에 캐시하기로 결정했습니다.이렇게 하면 우리는 이미지를 다시 다운로드하고 복호화할 필요가 없다.우리의 두 번째 적재 시간을 높이다.CacheStorage는 어느 곳에서든 접근할 수 있고 (메인 라인, 웹 워크맨, 서비스 종사자) 대상으로도 응답할 수 있기 때문에 이 점을 실현하는 이상적인 선택이다.우리는 아래의 몇 줄의 코드를 사용하여 전체 과정을 실현하였다.
// Open cache named `thumbs`
const cache = await caches.open('thumbs');

// Check if we already have thumbnail for the file in cache
const cacheResp: Response = await cache.match(file.id.toString());

if (cacheResp) {
    // Return cached response
    return URL.createObjectURL(await cacheResp.blob());
}

// We don't have it in cache. Lets fetch and decrypt it
const fileData = await fetch(`https://sever/path/to/file?id=${file.id}`);
const decrypted = await worker.decrypt(fileData, file.nonce, file.key);

// Put it in cache for future use
await cache.put(
    file.id.toString(),
    new Response(new Blob([decrypted]))
);

// Return the object URL
return URL.createObjectURL(new Blob([decrypted]));
이 밖에 갤러리의 양호한 사용자 체험도 없어서는 안 된다.이것은 사용자의 상호작용이 가장 빈번한 부분이다.슬라이딩, 축소, 이동 등 본체 프로그램의 모든 제스처를 지원하고 데스크톱과 모바일 장치로 확장할 수 있기를 바랍니다.이를 위해 우리는 많은 소스 라이브러리를 살펴보았지만, 그 중 대다수 라이브러리의 UX를 좋아하지 않았고, 모든 라이브러리가 희생되었다는 것을 발견했다.마지막으로 우리는 구매하기로 결정했다PhotoSwipe.그것은 우리의 대다수 용례에 적합하다.유일하게 부족한 것은 페이지의 무한 불러오기입니다.
비록 그들이 사이트에서 그것을 하나의 기능으로 열거했지만, 우리는 이미 불러온 이미지가 DOM에서 삭제되지 않고 새로운 페이지를 추가한 것을 발견했다.이것은 우리에게 결코 이상적이지 않다. 왜냐하면 한 사용자가 수천 장의 사진을 가질 수 있기 때문에, 우리는 그가 이 사진들을 신속하게 조회할 수 있기를 바란다.따라서, 우리는 react-window와 CSS 격자를 사용하여 우리의 갤러리 레이아웃을 만들고, 포토스weep는 사용자가 그림을 눌렀을 때 상호작용을 처리하도록 합니다.우리 응용 프로그램의 활약을 돋보이게 하다.
우리는 NextJS를 개봉 즉시 사용할 수 있는 디저트 SSR로 사용합니다.

근데 우리 아직 안 끝났어.😶


모든 것이 순조롭게 진행되었고, 우리는 심지어 베타 버전을 출시하여 일부 사용자에게 테스트를 제공하기도 했다. 당시 우리는 브라우저에서 무작위 탭이 붕괴되는 것을 보기 시작했다.프로그램에 메모리 유출이 있을 거야.
이 행위를 분석한 후에 내 친구가 동영상 파일을 열려고 했을 때 이런 행위가 일어났다는 것을 알아차렸다.그리고 나서 그것이 나를 명중시켰다. 우리의 모든 암호화와 복호화는 메모리에서 발생했다.이것은 내가 테스트한 작은 파일로 말하자면 이미 충분하다.그러나 큰 파일에 대해서는 메모리 제한이 다 소모되기 때문에 옵션이 붕괴될 것이다.
우리는 곧 파일 크기를 검사했는데 크기가 400MB에서 1GB 사이인 것을 발견했다.이것은 인터넷상에서 통하지 않는다.응용 프로그램에서 우리는 파일 시스템에 접근할 수 있기 때문에 블록별로 그것을 처리하고 파일에 추가할 수 있다.그러나 네트워크에서는 파일 시스템에 액세스할 수 없으므로 다른 방법이 필요합니다.우리는 모든 것을 크롬에서만 일하는 실험적인 API 뒤에 놓고 싶지 않다.

그래서 계속 찾고 있습니다.다행히도 나는 우연히 이 훌륭한 아나운서를 발견했다.
이게 답이야!우리는 Readable Streams를 사용하여 모든 내용을 메모리에 넣고 파일을 암호화/복호화하는 것이 아니라 암호화/복호화할 수 있다.그러나 Axios(API 호출을 위한 라이브러리)는 이를 지원하지 않습니다.따라서 API를 가져오는 데 도움을 청해야 합니다.나는 이것이 괜찮은 타협이라고 생각한다.
마지막으로 코드를 다음과 같이 재구성했습니다.
// Get the file
const resp = await fetch(`https://sever/path/to/file?id=${file.id}`);

// Get reader to be used in readable stream.
const reader = resp.body.getReader();

// Create a readable stream.
const stream = new ReadableStream({
    async start(controller) {
        // The following function handles each data chunk
        function push() {
            reader.read().then(async ({ done, value }) => {
                if (!done) {
                    // Decrypt chunk
                    const decryptedData = await worker.decryptChunk(value);

                    // Add decrypted data to stream
                    controller.enqueue(decryptedData);

                    // Is there more data to read?
                    push();
                } else {
                    // All done, rest!
                    controller.close();
                }
            });
        };

        push();
    }
});
return URL.createObjectURL(await new Response(stream).blob());
나는 여전히 이것이 효과가 있는지 의심스럽다.그러나 동영상이 불러올 때 라벨이 붕괴되지 않으면 나는 일곱 번째 천국에 들어간다.

제가 자기 전에 몇 마일이 남았어요.🚶‍♂️



나는 우리가 이 프로젝트에서 얻은 진전에 대해 기쁘다.나는 이 기술들의 존재와 그것을 어떻게 사용하는지 깨달았다.하지만 이를 실현하는 것은 완전히 다른 게임이다.나는 같은 것을 실현하기 위해 다시 쓰거나 더 좋은 방법을 찾아야 하는 경우가 여러 번 있었다. 왜냐하면 누드 기기는 유지하기 어려웠기 때문이다.WebWorkers,Comlink,CacheStorage,ReadableStream에 대해 알아봤습니다.여러 도서관과 협력하여 부족한 점을 기입하다.이 모든 것은 사용자의 체험, 가용성, 성능에 영향을 주지 않는다.
하지만 내가 해결하고 싶은 일은 드물다.현재 전체 동영상을 다운로드해야만 재생할 수 있다.이상적인 상황에서 나는 그것이 흐를 수 있기를 바란다.이를 위해 나는 시도했다MediaSource.MediaSource는 디코더를 명시적으로 지정해야 합니다. 저는 없습니다.그래서 나는 갇혔다.만약 당신이 나에게 이 문제를 어떻게 해결하는지에 대해 어떤 생각이 있다면 나에게 알려주세요.나는 너의 소식을 매우 듣고 싶다.😊
공유는 하나의 기능이다. 나는 이것이 이 응용 프로그램에도 매우 중요하다고 생각한다.API 통합만 남았습니다.오프라인 캐시에 Service workerWorkbox를 추가하고 PWA로 변환하여 모바일 및 데스크톱에 설치하고 싶습니다.
이 모든 소스 코드는 사용할 수 있습니다 here.이 제품에 관심이 있다면 ente.io를 보십시오.😊

좋은 웹페이지 즐겨찾기