download.js

import { __awaiter, __generator } from "tslib";
import { isBlob, isPromiseLike } from 'ut2';
import dataURLToBlob from './dataURLToBlob';
import isUrl from './isUrl';
import ajax from './ajax';
import { createObjectURL, nativeUndefined, revokeObjectURL } from './utils/native';
// 下载文件到本地
function saver(blobUrl, fileName) {
    if (fileName === void 0) { fileName = ''; }
    var anchor = document.createElement('a');
    // anchor.href = decodeURIComponent(blobUrl);
    anchor.href = blobUrl;
    anchor.style.display = 'none';
    anchor.setAttribute('download', fileName);
    // 处理点击事件,防止事件冒泡到 body/html 的点击事件。
    function handleClick(e) {
        e.stopPropagation();
        anchor.removeEventListener('click', handleClick);
    }
    anchor.addEventListener('click', handleClick);
    document.body.appendChild(anchor);
    anchor.click();
    document.body.removeChild(anchor);
}
/**
 * @callback TransformRequest
 * @param {AjaxOptions} options ajax 配置项
 * @returns {AjaxOptions | Promise<AjaxOptions>}
 */
/**
 * @callback TransformResponse
 * @param {Blob} res 响应的Blob对象。如果你通过 transformRequest 修改了 responseType ,该参数将是该类型响应值。
 * @returns {Blob | Promise<Blob>}
 */
/**
 * @typedef {Object} DownloadOptions 下载配置项
 * @property {string} [options.fileName] 文件名称
 * @property {string} [options.type] MIME 类型
 * @property {'url'|'text'} [options.dataType] 手动设置数据类型,默认会根据传入的数据判断类型,主要是为了区分 url 和 text 。<br/>如果你要下载的文本是 url ,请设置 'text' ;如果你要下载的 url 是绝对/相对路径,请设置 'url' 。
 * @property {TransformRequest} [options.transformRequest] 请求前触发,XHR 对象或配置调整
 * @property {TransformResponse} [options.transformResponse] 请求成功后触发,在传递给 then/catch 前,允许修改响应数据
 */
/**
 * 下载
 *
 * <em style="font-weight: bold;">注意:该方法仅适用于浏览器端,兼容 IE10+ 和现代浏览器。</em>
 *
 * <em style="font-weight: bold;">注意:微信浏览器不支持H5下载文件。</em>
 *
 * @static
 * @alias module:Other.download
 * @since 4.16.0
 * @see {@link https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Access-Control-Expose-Headers | Access-Control-Expose-Headers}
 * @see {@link https://zh.wikipedia.org/wiki/多用途互聯網郵件擴展 | MIME}
 * @see {@link https://9ykc9s.csb.app/ | 在线示例}
 * @param {string|Blob|ArrayBuffer|TypedArray} data 字符串、blob数据或url地址
 * @param {string|DownloadOptions} [options] 文件名称 或 配置项
 * @returns {Promise<void>}
 * @example
 * // 文本
 * download('hello world', 'text.txt');
 *
 * // 远程文件1
 * // 不带协议的绝对地址,需要通过 dataType 指定为 url 类型
 * download('/xxx.jpg', { dataType: 'url', fileName: 'test.jpg' });
 *
 * // 远程文件2
 * download('https://example.com/xxx.jpg');
 *
 * // base64
 * download('data:image/png;base64,PGEgaWQ9ImEiPjxiIGlkPSJiIj5oZXkhPC9iPjwvYT4=', 'test.png');
 *
 * // blob文件
 * download(new Blob(['hello world']), 'text.txt');
 *
 * // 本地文件
 * download(File, 'filename.ext');
 *
 */
function download(data, options) {
    return __awaiter(this, void 0, void 0, function () {
        var config, fileName, type, dataType, transformRequest, transformResponse, payload, asyncTransformRequest, asyncTransformResponse, ajaxOptions, ev, res, currentFileName, url;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    config = typeof options === 'object' ? options : {};
                    if (typeof options === 'string') {
                        config.fileName = options;
                    }
                    fileName = config.fileName, type = config.type, dataType = config.dataType, transformRequest = config.transformRequest, transformResponse = config.transformResponse;
                    if (!(typeof data === 'string')) return [3 /*break*/, 8];
                    if (!(!dataType && /^blob:.*?\/.*/.test(data))) return [3 /*break*/, 1];
                    // blob url
                    saver(data, fileName);
                    return [2 /*return*/, Promise.resolve()];
                case 1:
                    if (!(!dataType && /^data:([\w+-]+\/[\w+.-]+)?[,;]/.test(data))) return [3 /*break*/, 2];
                    // dataURLs
                    payload = dataURLToBlob(data);
                    return [3 /*break*/, 7];
                case 2:
                    if (!(dataType === 'url' || (!dataType && isUrl(data)))) return [3 /*break*/, 6];
                    asyncTransformRequest = function (opts) {
                        // 请求前配置调整
                        var tempOptions = typeof transformRequest === 'function' ? transformRequest(opts) : opts;
                        return isPromiseLike(tempOptions) ? tempOptions : Promise.resolve(tempOptions);
                    };
                    asyncTransformResponse = function (res) {
                        var tempRes = typeof transformResponse === 'function' ? transformResponse(res) : res;
                        return isPromiseLike(tempRes) ? tempRes : Promise.resolve(tempRes);
                    };
                    return [4 /*yield*/, asyncTransformRequest({ responseType: 'blob' })];
                case 3:
                    ajaxOptions = _a.sent();
                    return [4 /*yield*/, ajax(data, ajaxOptions)];
                case 4:
                    ev = _a.sent();
                    return [4 /*yield*/, asyncTransformResponse(ev.target.response)];
                case 5:
                    res = _a.sent();
                    currentFileName = fileName || data.split('?')[0].split('#')[0].split('/').pop();
                    return [2 /*return*/, download(res, { fileName: currentFileName, type: type || (isBlob(res) ? res.type : nativeUndefined) })];
                case 6:
                    // string
                    payload = new Blob([data], { type: type || 'text/plain' });
                    _a.label = 7;
                case 7: return [3 /*break*/, 9];
                case 8:
                    if (data instanceof Blob) {
                        // File or Blob
                        payload = data;
                    }
                    _a.label = 9;
                case 9:
                    // html、TypedArray
                    if (!payload) {
                        payload = new Blob([data], { type: type });
                    }
                    // @ts-ignore
                    if (navigator.msSaveBlob) {
                        // @ts-ignore
                        navigator.msSaveBlob(payload, fileName || 'download');
                    }
                    else {
                        url = createObjectURL(payload);
                        saver(url, fileName);
                        revokeObjectURL(url);
                    }
                    return [2 /*return*/, Promise.resolve()];
            }
        });
    });
}
export default download;