如何在html / javascript中加快从本地文件系统中显示大图像的速度

时间:2016-03-04 23:48:13

标签: javascript html angularjs typescript rxjs

我有一个html应用程序,我正在处理大量的大图像。我们正在讨论可能有5,000张照片,每张照片大约3-5MB。

到目前为止,我正在测试大约1000张图片,而且事情已经变得很慢了。

我正在使用拖放和FileReader加载图像,然后将FileReader结果设置为图像源:

$ sdiff -t prime.S prime_int.S 

prime.o:     file format elf64-x86-64                           |  prime_int.o:     file format elf64-x86-64


Disassembly of section .text:                                      Disassembly of section .text:

0000000000000000 <isPrime>:                                        0000000000000000 <isPrime>:
#else                                                              #else
typedef unsigned long long NUM_TYPE ;                              typedef unsigned long long NUM_TYPE ;
#endif                                                             #endif

int isPrime(NUM_TYPE number)                                       int isPrime(NUM_TYPE number)
{                                                                  {
   0:   55                      push   %rbp                           0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp                      1:   48 89 e5                mov    %rsp,%rbp
   4:   48 83 ec 50             sub    $0x50,%rsp                     4:   48 83 ec 50             sub    $0x50,%rsp
   8:   48 89 7d f0             mov    %rdi,-0x10(%rbp)         |     8:   89 7d f8                mov    %edi,-0x8(%rbp)

        if (number < 2) return 0;                                          if (number < 2) return 0;
   c:   48 83 7d f0 02          cmpq   $0x2,-0x10(%rbp)         |     b:   83 7d f8 02             cmpl   $0x2,-0x8(%rbp)
  11:   0f 83 0c 00 00 00       jae    23 <isPrime+0x23>        |     f:   0f 8d 0c 00 00 00       jge    21 <isPrime+0x21>
  17:   c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%rbp)          |    15:   c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%rbp)
  1e:   e9 c1 01 00 00          jmpq   1e4 <isPrime+0x1e4>      |    1c:   e9 88 01 00 00          jmpq   1a9 <isPrime+0x1a9>
        if (number == 2) return 1;                                         if (number == 2) return 1;
  23:   48 83 7d f0 02          cmpq   $0x2,-0x10(%rbp)         |    21:   83 7d f8 02             cmpl   $0x2,-0x8(%rbp)
  28:   0f 85 0c 00 00 00       jne    3a <isPrime+0x3a>        |    25:   0f 85 0c 00 00 00       jne    37 <isPrime+0x37>
  2e:   c7 45 fc 01 00 00 00    movl   $0x1,-0x4(%rbp)          |    2b:   c7 45 fc 01 00 00 00    movl   $0x1,-0x4(%rbp)
  35:   e9 aa 01 00 00          jmpq   1e4 <isPrime+0x1e4>      |    32:   e9 72 01 00 00          jmpq   1a9 <isPrime+0x1a9>
        if (number == 3) return 1;                                         if (number == 3) return 1;
  3a:   48 83 7d f0 03          cmpq   $0x3,-0x10(%rbp)         |    37:   83 7d f8 03             cmpl   $0x3,-0x8(%rbp)
  3f:   0f 85 0c 00 00 00       jne    51 <isPrime+0x51>        |    3b:   0f 85 0c 00 00 00       jne    4d <isPrime+0x4d>
  45:   c7 45 fc 01 00 00 00    movl   $0x1,-0x4(%rbp)          |    41:   c7 45 fc 01 00 00 00    movl   $0x1,-0x4(%rbp)
  4c:   e9 93 01 00 00          jmpq   1e4 <isPrime+0x1e4>      |    48:   e9 5c 01 00 00          jmpq   1a9 <isPrime+0x1a9>
        if (number == 5) return 1;                                         if (number == 5) return 1;
  51:   48 83 7d f0 05          cmpq   $0x5,-0x10(%rbp)         |    4d:   83 7d f8 05             cmpl   $0x5,-0x8(%rbp)
  56:   0f 85 0c 00 00 00       jne    68 <isPrime+0x68>        |    51:   0f 85 0c 00 00 00       jne    63 <isPrime+0x63>
  5c:   c7 45 fc 01 00 00 00    movl   $0x1,-0x4(%rbp)          |    57:   c7 45 fc 01 00 00 00    movl   $0x1,-0x4(%rbp)
  63:   e9 7c 01 00 00          jmpq   1e4 <isPrime+0x1e4>      |    5e:   e9 46 01 00 00          jmpq   1a9 <isPrime+0x1a9>
                                                                >    63:   b8 02 00 00 00          mov    $0x2,%eax
        if (!(number % 2)) return 0;                                       if (!(number % 2)) return 0;
  68:   48 8b 45 f0             mov    -0x10(%rbp),%rax         |    68:   8b 4d f8                mov    -0x8(%rbp),%ecx
  6c:   48 83 e0 01             and    $0x1,%rax                |    6b:   89 45 c0                mov    %eax,-0x40(%rbp)
  70:   48 83 f8 00             cmp    $0x0,%rax                |    6e:   89 c8                   mov    %ecx,%eax
  74:   0f 85 0c 00 00 00       jne    86 <isPrime+0x86>        |    70:   99                      cltd   
  7a:   c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%rbp)          |    71:   8b 4d c0                mov    -0x40(%rbp),%ecx
  81:   e9 5e 01 00 00          jmpq   1e4 <isPrime+0x1e4>      |    74:   f7 f9                   idiv   %ecx
  86:   b8 03 00 00 00          mov    $0x3,%eax                |    76:   83 fa 00                cmp    $0x0,%edx
  8b:   89 c1                   mov    %eax,%ecx                |    79:   0f 85 0c 00 00 00       jne    8b <isPrime+0x8b>
                                                                >    7f:   c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%rbp)
                                                                >    86:   e9 1e 01 00 00          jmpq   1a9 <isPrime+0x1a9>
                                                                >    8b:   b8 03 00 00 00          mov    $0x3,%eax
        if (!(number % 3)) return 0;                                       if (!(number % 3)) return 0;
  8d:   48 8b 45 f0             mov    -0x10(%rbp),%rax         |    90:   8b 4d f8                mov    -0x8(%rbp),%ecx
  91:   31 d2                   xor    %edx,%edx                |    93:   89 45 bc                mov    %eax,-0x44(%rbp)
  93:   48 f7 f1                div    %rcx                     |    96:   89 c8                   mov    %ecx,%eax
  96:   48 83 fa 00             cmp    $0x0,%rdx                |    98:   99                      cltd   
  9a:   0f 85 0c 00 00 00       jne    ac <isPrime+0xac>        |    99:   8b 4d bc                mov    -0x44(%rbp),%ecx
  a0:   c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%rbp)          |    9c:   f7 f9                   idiv   %ecx
  a7:   e9 38 01 00 00          jmpq   1e4 <isPrime+0x1e4>      |    9e:   83 fa 00                cmp    $0x0,%edx
  ac:   b8 05 00 00 00          mov    $0x5,%eax                |    a1:   0f 85 0c 00 00 00       jne    b3 <isPrime+0xb3>
  b1:   89 c1                   mov    %eax,%ecx                |    a7:   c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%rbp)
                                                                >    ae:   e9 f6 00 00 00          jmpq   1a9 <isPrime+0x1a9>
                                                                >    b3:   b8 05 00 00 00          mov    $0x5,%eax
        if (!(number % 5)) return 0;                                       if (!(number % 5)) return 0;
  b3:   48 8b 45 f0             mov    -0x10(%rbp),%rax         |    b8:   8b 4d f8                mov    -0x8(%rbp),%ecx
  b7:   31 d2                   xor    %edx,%edx                |    bb:   89 45 b8                mov    %eax,-0x48(%rbp)
  b9:   48 f7 f1                div    %rcx                     |    be:   89 c8                   mov    %ecx,%eax
  bc:   48 83 fa 00             cmp    $0x0,%rdx                |    c0:   99                      cltd   
  c0:   0f 85 0c 00 00 00       jne    d2 <isPrime+0xd2>        |    c1:   8b 4d b8                mov    -0x48(%rbp),%ecx
  c6:   c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%rbp)          |    c4:   f7 f9                   idiv   %ecx
  cd:   e9 12 01 00 00          jmpq   1e4 <isPrime+0x1e4>      |    c6:   83 fa 00                cmp    $0x0,%edx
                                                                >    c9:   0f 85 0c 00 00 00       jne    db <isPrime+0xdb>
                                                                >    cf:   c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%rbp)
                                                                >    d6:   e9 ce 00 00 00          jmpq   1a9 <isPrime+0x1a9>
        if (number < 7*7) return 1;                                        if (number < 7*7) return 1;
  d2:   48 83 7d f0 31          cmpq   $0x31,-0x10(%rbp)        |    db:   83 7d f8 31             cmpl   $0x31,-0x8(%rbp)
  d7:   0f 83 0c 00 00 00       jae    e9 <isPrime+0xe9>        |    df:   0f 8d 0c 00 00 00       jge    f1 <isPrime+0xf1>
  dd:   c7 45 fc 01 00 00 00    movl   $0x1,-0x4(%rbp)          |    e5:   c7 45 fc 01 00 00 00    movl   $0x1,-0x4(%rbp)
  e4:   e9 fb 00 00 00          jmpq   1e4 <isPrime+0x1e4>      |    ec:   e9 b8 00 00 00          jmpq   1a9 <isPrime+0x1a9>
        int step[] = {7,11,13,17,19,23,29,31};                             int step[] = {7,11,13,17,19,23,29,31};
  e9:   0f 28 05 00 00 00 00    movaps 0x0(%rip),%xmm0        # |    f1:   48 8b 04 25 00 00 00    mov    0x0,%rax
  f0:   0f 29 45 e0             movaps %xmm0,-0x20(%rbp)        |    f8:   00 
  f4:   0f 28 05 00 00 00 00    movaps 0x0(%rip),%xmm0        # |    f9:   48 89 45 d0             mov    %rax,-0x30(%rbp)
  fb:   0f 29 45 d0             movaps %xmm0,-0x30(%rbp)        |    fd:   48 8b 04 25 00 00 00    mov    0x0,%rax
                                                                >   104:   00 
                                                                >   105:   48 89 45 d8             mov    %rax,-0x28(%rbp)
                                                                >   109:   48 8b 04 25 00 00 00    mov    0x0,%rax
                                                                >   110:   00 
                                                                >   111:   48 89 45 e0             mov    %rax,-0x20(%rbp)
                                                                >   115:   48 8b 04 25 00 00 00    mov    0x0,%rax
                                                                >   11c:   00 
                                                                >   11d:   48 89 45 e8             mov    %rax,-0x18(%rbp)
        int sentry = (NUM_TYPE)sqrt((double)number);                       int sentry = (NUM_TYPE)sqrt((double)number);
  ff:   0f 28 05 00 00 00 00    movaps 0x0(%rip),%xmm0        # |   121:   f2 0f 2a 45 f8          cvtsi2sdl -0x8(%rbp),%xmm0
 106:   f3 0f 7e 4d f0          movq   -0x10(%rbp),%xmm1        |   126:   e8 00 00 00 00          callq  12b <isPrime+0x12b>
 10b:   66 0f 62 c8             punpckldq %xmm0,%xmm1           |   12b:   f2 0f 2c c8             cvttsd2si %xmm0,%ecx
 10f:   66 0f 28 05 00 00 00    movapd 0x0(%rip),%xmm0        # |   12f:   89 4d cc                mov    %ecx,-0x34(%rbp)
 116:   00                                                      <
 117:   66 0f 5c c8             subpd  %xmm0,%xmm1              <
 11b:   66 0f 70 c1 4e          pshufd $0x4e,%xmm1,%xmm0        <
 120:   66 0f 58 c1             addpd  %xmm1,%xmm0              <
 124:   e8 00 00 00 00          callq  129 <isPrime+0x129>      <
 129:   f2 0f 10 0d 00 00 00    movsd  0x0(%rip),%xmm1        # <
 130:   00                                                      <
 131:   0f 28 d0                movaps %xmm0,%xmm2              <
 134:   f2 0f 5c d1             subsd  %xmm1,%xmm2              <
 138:   f2 48 0f 2c c2          cvttsd2si %xmm2,%rax            <
 13d:   48 b9 00 00 00 00 00    movabs $0x8000000000000000,%rcx <
 144:   00 00 80                                                <
 147:   48 31 c8                xor    %rcx,%rax                <
 14a:   f2 48 0f 2c c8          cvttsd2si %xmm0,%rcx            <
 14f:   66 0f 2e c1             ucomisd %xmm1,%xmm0             <
 153:   48 0f 42 c1             cmovb  %rcx,%rax                <
 157:   89 c2                   mov    %eax,%edx                <
 159:   89 55 cc                mov    %edx,-0x34(%rbp)         <
        for(int r = 0; r < sentry; r += 30)                                for(int r = 0; r < sentry; r += 30)
 15c:   c7 45 c8 00 00 00 00    movl   $0x0,-0x38(%rbp)         |   132:   c7 45 c8 00 00 00 00    movl   $0x0,-0x38(%rbp)
 163:   8b 45 c8                mov    -0x38(%rbp),%eax         |   139:   8b 45 c8                mov    -0x38(%rbp),%eax
 166:   3b 45 cc                cmp    -0x34(%rbp),%eax         |   13c:   3b 45 cc                cmp    -0x34(%rbp),%eax
 169:   0f 8d 6e 00 00 00       jge    1dd <isPrime+0x1dd>      |   13f:   0f 8d 5d 00 00 00       jge    1a2 <isPrime+0x1a2>
            for(int i = 0; i < 8; ++i)                                         for(int i = 0; i < 8; ++i)
 16f:   c7 45 c4 00 00 00 00    movl   $0x0,-0x3c(%rbp)         |   145:   c7 45 c4 00 00 00 00    movl   $0x0,-0x3c(%rbp)
 176:   83 7d c4 08             cmpl   $0x8,-0x3c(%rbp)         |   14c:   83 7d c4 08             cmpl   $0x8,-0x3c(%rbp)
 17a:   0f 8d 4a 00 00 00       jge    1ca <isPrime+0x1ca>      |   150:   0f 8d 39 00 00 00       jge    18f <isPrime+0x18f>
                if (!(number % (r+step[i])))                                       if (!(number % (r+step[i])))
 180:   48 8b 45 f0             mov    -0x10(%rbp),%rax         |   156:   8b 45 f8                mov    -0x8(%rbp),%eax
 184:   8b 4d c8                mov    -0x38(%rbp),%ecx         |   159:   8b 4d c8                mov    -0x38(%rbp),%ecx
 187:   48 63 55 c4             movslq -0x3c(%rbp),%rdx         |   15c:   48 63 55 c4             movslq -0x3c(%rbp),%rdx
 18b:   03 4c 95 d0             add    -0x30(%rbp,%rdx,4),%ecx  |   160:   03 4c 95 d0             add    -0x30(%rbp,%rdx,4),%ecx
 18f:   48 63 d1                movslq %ecx,%rdx                |   164:   99                      cltd   
 192:   31 c9                   xor    %ecx,%ecx                |   165:   f7 f9                   idiv   %ecx
 194:   48 89 55 b8             mov    %rdx,-0x48(%rbp)         |   167:   83 fa 00                cmp    $0x0,%edx
 198:   89 ca                   mov    %ecx,%edx                |   16a:   0f 85 0c 00 00 00       jne    17c <isPrime+0x17c>
 19a:   48 8b 75 b8             mov    -0x48(%rbp),%rsi         <
 19e:   48 f7 f6                div    %rsi                     <
 1a1:   48 83 fa 00             cmp    $0x0,%rdx                <
 1a5:   0f 85 0c 00 00 00       jne    1b7 <isPrime+0x1b7>      <
                    return 0;                                                          return 0;
 1ab:   c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%rbp)          |   170:   c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%rbp)
 1b2:   e9 2d 00 00 00          jmpq   1e4 <isPrime+0x1e4>      |   177:   e9 2d 00 00 00          jmpq   1a9 <isPrime+0x1a9>
        if (number < 7*7) return 1;                                        if (number < 7*7) return 1;
        int step[] = {7,11,13,17,19,23,29,31};                             int step[] = {7,11,13,17,19,23,29,31};
        int sentry = (NUM_TYPE)sqrt((double)number);                       int sentry = (NUM_TYPE)sqrt((double)number);
        for(int r = 0; r < sentry; r += 30)                                for(int r = 0; r < sentry; r += 30)
            for(int i = 0; i < 8; ++i)                                         for(int i = 0; i < 8; ++i)
                if (!(number % (r+step[i])))                                       if (!(number % (r+step[i])))
 1b7:   e9 00 00 00 00          jmpq   1bc <isPrime+0x1bc>      |   17c:   e9 00 00 00 00          jmpq   181 <isPrime+0x181>
        if (!(number % 5)) return 0;                                       if (!(number % 5)) return 0;
        if (number < 7*7) return 1;                                        if (number < 7*7) return 1;
        int step[] = {7,11,13,17,19,23,29,31};                             int step[] = {7,11,13,17,19,23,29,31};
        int sentry = (NUM_TYPE)sqrt((double)number);                       int sentry = (NUM_TYPE)sqrt((double)number);
        for(int r = 0; r < sentry; r += 30)                                for(int r = 0; r < sentry; r += 30)
            for(int i = 0; i < 8; ++i)                                         for(int i = 0; i < 8; ++i)
 1bc:   8b 45 c4                mov    -0x3c(%rbp),%eax         |   181:   8b 45 c4                mov    -0x3c(%rbp),%eax
 1bf:   83 c0 01                add    $0x1,%eax                |   184:   83 c0 01                add    $0x1,%eax
 1c2:   89 45 c4                mov    %eax,-0x3c(%rbp)         |   187:   89 45 c4                mov    %eax,-0x3c(%rbp)
 1c5:   e9 ac ff ff ff          jmpq   176 <isPrime+0x176>      |   18a:   e9 bd ff ff ff          jmpq   14c <isPrime+0x14c>
                if (!(number % (r+step[i])))                                       if (!(number % (r+step[i])))
                    return 0;                                                          return 0;
 1ca:   e9 00 00 00 00          jmpq   1cf <isPrime+0x1cf>      |   18f:   e9 00 00 00 00          jmpq   194 <isPrime+0x194>
        if (!(number % 3)) return 0;                                       if (!(number % 3)) return 0;
        if (!(number % 5)) return 0;                                       if (!(number % 5)) return 0;
        if (number < 7*7) return 1;                                        if (number < 7*7) return 1;
        int step[] = {7,11,13,17,19,23,29,31};                             int step[] = {7,11,13,17,19,23,29,31};
        int sentry = (NUM_TYPE)sqrt((double)number);                       int sentry = (NUM_TYPE)sqrt((double)number);
        for(int r = 0; r < sentry; r += 30)                                for(int r = 0; r < sentry; r += 30)
 1cf:   8b 45 c8                mov    -0x38(%rbp),%eax         |   194:   8b 45 c8                mov    -0x38(%rbp),%eax
 1d2:   83 c0 1e                add    $0x1e,%eax               |   197:   83 c0 1e                add    $0x1e,%eax
 1d5:   89 45 c8                mov    %eax,-0x38(%rbp)         |   19a:   89 45 c8                mov    %eax,-0x38(%rbp)
 1d8:   e9 86 ff ff ff          jmpq   163 <isPrime+0x163>      |   19d:   e9 97 ff ff ff          jmpq   139 <isPrime+0x139>
            for(int i = 0; i < 8; ++i)                                         for(int i = 0; i < 8; ++i)
                if (!(number % (r+step[i])))                                       if (!(number % (r+step[i])))
                    return 0;                                                          return 0;
        return 1;                                                          return 1;
 1dd:   c7 45 fc 01 00 00 00    movl   $0x1,-0x4(%rbp)          |   1a2:   c7 45 fc 01 00 00 00    movl   $0x1,-0x4(%rbp)
}                                                                  }
 1e4:   8b 45 fc                mov    -0x4(%rbp),%eax          |   1a9:   8b 45 fc                mov    -0x4(%rbp),%eax
 1e7:   48 83 c4 50             add    $0x50,%rsp               |   1ac:   48 83 c4 50             add    $0x50,%rsp
 1eb:   5d                      pop    %rbp                     |   1b0:   5d                      pop    %rbp
 1ec:   c3                      retq                            |   1b1:   c3                      retq   

HTML:

    private loadImageFromDisk(image: IImage): Rx.Observable<IImage> {

        return Rx.Observable.defer( () => {
            console.log( `loading ${image.file.name} from disc` );
            console.time( `file ${image.file.name} loaded from file system` );

            const reader = new FileReader();
            setTimeout( () => reader.readAsDataURL(image.file), 0 ) ;

            const subject = new Rx.Subject();

            reader.onload = event => {
                subject.onNext( reader.result );
                subject.onCompleted();
            }

            return subject
                .safeApply(
                    this.$rootScope,
                    result => {
                        console.timeEnd( `file ${image.file.name} loaded from file system` );
                        image.content = reader.result;
                    }
                )
                .flatMap( result => Rx.Observable.return( image ) );
        } );
    }

我知道ng-repeat可能是一个性能问题,我会对此进行排序,但目前即使显示一个图像也需要几秒钟。如果我从光盘加载图像但实际上没有显示它,则每张图像只需要大约50-100毫秒即可从光盘加载。如果我显示它,事情变得更慢。

我怀疑减速是浏览器(chrome)必须调整图像大小。

在我用70张图像进行的测试中,我将所有这些图像加载到浏览器中,在加载完所有内容后,滚动性能很慢,前几次我在页面上下滚动,之后就很顺利。

这些图像大约3,000像素乘2,000。我将它们调整为200像素长以显示它们。

加快这一过程的最佳方法是什么?

1 个答案:

答案 0 :(得分:1)

我前段时间遇到同样的问题(为摄影师服务时,使用角度)。

问题与RxJS或角度有关,而不是浏览器本身 - 它没有针对以这种方式显示大量图像进行优化。

首先,如果您需要显示大量图像(无论是本地文件还是远程文件):

  1. 在显示之前调整它们的大小(加载速度更快,不需要调整大小,降低内存消耗)。
  2. 如果可以 - 只显示可见图像(否则在加载所有图像之前页面会非常慢)。请检查此答案:How do I get the x and y positions of an element in an AngularJS directive原来是trackVisibility,只有在可见时才会显示图片。
  3. 关于从本地文件显示图像,事情变得更加复杂:

    在您的情况下,您将文件作为数据网址加载,并且存在一个问题:您提到的每个3 MB的70个图像将消耗至少2.1 Gb的RAM(实际上更多,并且不经意地会影响性能)

    首先建议 - 如果您可以:不使用数据网址,请在不再需要时使用URL.createObjectURL并使用URL.revokeObjectURL

    第二:如果你只需要缩略图 - 在显示之前在本地(使用画布)调整图像大小。如果对你来说很重要,那么抗锯齿就会出现问题 - 请看一下这里描述的降压技术:Html5 canvas drawImage: how to apply antialiasing如果你支持iOS,那么画布尺寸限制就会出现问题,所以你需要以某种方式检测它。 (这两个问题都在下面的例子中讨论过)

    最后一个:如果您需要为大量图像创建缩略图 - 请不要立即执行此操作,而是通过事件循环计划工件(否则在调整图像大小时浏览器不会响应)。并且为了更好的性能:按顺序执行(不是对所有图像并行执行),听起来可能很奇怪 - 但是,它会更快(因为内存消耗太低,同时磁盘读取次数更少)。

    总结:

    1. 使用上面提到的trackVisibility指令仅显示可见图像
    2. 请勿使用数据网址,特别是大图片。
    3. 在显示之前创建已调整大小的缩略图
    4. 您可能会发现有助于实现此目的的库:

      关于执行图像缩略图的粗略代码示例(大部分代码都是从工作项目中复制的 - 因此,预计它会起作用。canvasToJpegBlobmakeThumbnail刚刚编写并且未经过测试,所以可能是小错误):

       function loadImage(imagePath) {
         return Rx.Observable.create(function(observer) {
           var img = new Image();
           img.src = imagePath;
           image.onload = function() {
             observer.onNext(image);
             observer.onCompleted();
           }
           image.onError = function(err) {
             observer.onError(err);
           }
         });
       }
      
       // canvas edge cases detection
       var maxDimm = 32000;
       var ios5 = false, ios3 = false;
       (function() {
         if (navigator.userAgent.indexOf('MSIE') !== -1 || navigator.appVersion.indexOf('Trident/') > 0) {
           maxDimm = 8000;
         } else {
           var canvas = document.createElement('canvas');
           canvas.width = 1024 * 3;
           canvas.height = 1025;
           if (canvas.toDataURL('image/jpeg') === 'data:,') {
             ios3 = true;
           } else {
             canvas = document.createElement('canvas');
             canvas.width = 1024 * 5;
             canvas.height = 1025;
             if (canvas.toDataURL('image/jpeg') === 'data:,') {
               ios5 = true;
             }
           }
         }
       }());
      
       function stepDown(src, width, height) {
         var
           steps,
           resultCanvas = document.createElement('canvas'),
           srcWidth = src.width,
           srcHeight = src.height,
           context;
      
         resultCanvas.width = width;
         resultCanvas.height = height;
      
         if ((srcWidth / width) > (srcHeight / height)) {
           steps = Math.ceil(Math.log(srcWidth / width) / Math.log(2));
         } else {
           steps = Math.ceil(Math.log(srcHeight / height) / Math.log(2));
         }
      
         if (steps <= 1) {
           context = resultCanvas.getContext('2d');
           context.drawImage(src, 0, 0, width, height);
         } else {
           var tmpCanvas = document.createElement('canvas');
      
           var
             currentWidth = width * Math.pow(2, steps - 1),
             currentHeight = height * Math.pow(2, steps - 1),
             newWidth = currentWidth,
             newHeight = currentHeight;
      
           if (ios3 && currentWidth * currentHeight > 3 * 1024 * 1024) {
             newHeight = 1024 * Math.sqrt(3 * srcHeight / srcWidth);
             newWidth = newHeight * srcWidth / srcHeight;
           } else {
             if (ios5 && currentWidth * currentHeight > 5 * 1024 * 1024) {
               newHeight = 1024 * Math.sqrt(5 * srcHeight / srcWidth);
               newWidth = newHeight * srcWidth / srcHeight;
             } else {
               if (currentWidth > maxDimm || currentHeight > maxDimm) {
                 if (currentHeight > currentWidth) {
                   newHeight = maxDimm;
                   newWidth = maxDimm * currentWidth / currentHeight;
                 } else {
                   newWidth = maxDimm;
                   newHeight = maxDimm * currentWidth / currentHeight;
                 }
               }
             }
           }
      
           currentWidth = newWidth;
           currentHeight = newHeight;
      
           if ((currentWidth / width) > (currentHeight / height)) {
             steps = Math.ceil(Math.log(currentWidth / width) / Math.log(2));
           } else {
             steps = Math.ceil(Math.log(currentHeight / height) / Math.log(2));
           }
      
      
           context = tmpCanvas.getContext('2d');
           tmpCanvas.width = Math.ceil(currentWidth);
           tmpCanvas.height = Math.ceil(currentHeight);
      
           context.drawImage(src, 0, 0, srcWidth, srcHeight, 0, 0, currentWidth, currentHeight);
      
           while (steps > 1) {
             newWidth = currentWidth * 0.5;
             newHeight = currentHeight * 0.5;
      
             context.drawImage(tmpCanvas, 0, 0, currentWidth, currentHeight, 0, 0, newWidth, newHeight);
             steps -= 1;
             currentWidth = newWidth;
             currentHeight = newHeight;
           }
      
           context = resultCanvas.getContext('2d');
           context.drawImage(tmpCanvas, 0, 0, currentWidth, currentHeight, 0, 0, width, height);
         }
         return resultCanvas;
       }
      
       function canvasToJpegBlob(canvas) {
         return Rx.Observable.create(function(observer) {
           try {
             canvas.toBlob(function(blob) {
               observer.onNext(blob);
               observer.onCompleted();
             }, 'image/jpeg');
           } catch (err) {
             observer.onError(err);
           }
         });
       }
      
       function makeThumbnail(file) {
         return Observable.defer(()=> {
           const fileUrl = URL.createObjectURL(file);
           return loadImage(fileUrl)
             .map(image => {
               const width = 200;
               const height = image.height * width / image.width;
               const thumbnailCanvas = stepDown(image, width, height);
               URL.revokeObjectURL(fileUrl);
               return thubnailCanvas;
             })
             .flatMap(canvasToJpegBlob)
             .map(canvasBlob=>URL.createObjectURL(canvasBlob))
             .map(thumbnailUrl => {
               return {
                 file,
                 thumbnailUrl
               }
             })
         });
        }