Sunshine168 / resume

My resume
3 stars 1 forks source link

图片文件和视频文件的属性获取 #18

Open Sunshine168 opened 5 years ago

Sunshine168 commented 5 years ago

前言

通过JS在浏览器里获取图片属性和视频属性的主要还是借助img和video标签在已有进行获取,而不是借助wasm等手段对文件进行完整解析。最终我们可以获取到图片的实际尺寸,视频的时长,分辨率(由视频的宽高组成)等属性

为什么会有这样的需要,源自于需求—— 是因为对这类的媒体文件是有限制的,如果上传完再进行解析,这里不仅需要等待而且使用者可能会发现自己辛辛苦苦(不看规则)上传视频和图片无效,然后需要重新上传,这样会有非常不好的体验。

所以为了解决这种问题,所以就有了我们需要在上传的时候做检测,当然这种检测是有局限性的,最好的办法是在服务端支持降级的方案,这里不展开。

需要注意的地方,由于我们是借助img标签和video标签进行解析,那么我们在我们支持的格式上就必须是所在浏览器支持的文件类型。

以视频文件为例子,Avi类型的视频文件就是不被支持的,而mp4和mov文件则是被支持良好的。

以图片文件为例tiff就是不被支持的,在safari上webp就是不被支持的。

思路:

我们需要借助的核心API

window.URL.createObjectURL

image

总体来说兼容性表现非常好 通过引用来介绍一下这个API

URL.createObjectURL() 静态方法会创建一个 DOMString,其中包含一个表示参数中给出的对象的URL。这个 URL 的生命周期和创建它的窗口中的 document 绑定。这个新的URL 对象表示指定的 File 对象或 Blob 对象。

大体来说就是传入Blob或者Blob的衍生子类,File是继承自Blob的,然后会返回一个url,这个是在当前窗口中可用的该文件的url。简单说来说就可以直接

<img src={url}/>

另外需要注意的是在使用完这个链接后,尽可能的主动通过 URL.revokeObjectURL()对资源进行释放,如果不主动释放的话,会在页面关闭的时候释放掉。

Code Snippet

//tool
const promiseTimer: promiseTimer = time => {
  return new Promise(resolve => setTimeout(resolve, time));
};

export const getImagePropertyFormFile = async file => {
  let url;
  let image;
  try {
    url = window.URL.createObjectURL(file);
    image = await getImage(url, 2000);
  } catch (e) {
    return {
      err: e
    };
  }
  // 返回图片的属性
  return {image};
};

export const getImage = (url, timeout) => {
  const loadImagePromise = new Promise(resolve => {
    const img = new Image();
    img.src = url;
    // onload说明加载完毕
    img.onload = () => {
      resolve(img);
    };
  });
  // 超时的时候方便重试和降级
  return Promise.race([loadImagePromise, promiseTimer(timeout)]);
};
export const getVideo = (url, timeout) => {
  const loadVideoPromise: Promise<IVideoTagInfo> = new Promise(resolve => {
    const video = document.createElement('video');
    //仅预加载视频的元信息
    video.preload = 'metadata';
    //onloadedmetadata 说明这个视频元信息加载完毕
    video.onloadedmetadata = () => {
      resolve(video);
    };
    video.src = url;
  });
  return Promise.race([loadVideoPromise, promiseTimer(timeout)]);
};

具体业务不一样,就不针对整个获取信息并检验以及支持降级的插件进行说明了