关于Ajax无法下载文件到浏览器本地的问题(文件下载的几种方式)

最近在做网站的时候遇到这样一个功能,在如图所示的页面中,需要用户点击链接的时候,能够以异步Ajax的方式判断服务器中是否存储有相应的Excel文件,如果没有的话就提示用户没有找到,如果有的话就下载到用户本地。

当然,这是很简单的一个问题,按照一般方式编写Ajax就可以了。但是当服务器端把文件内容以二进制的形式返回到浏览器端,浏览器的Ajax却抛出了错误。大致是ParseError, Invalid XML PK一类的错误信息。

造成这个问题的原因,并不是服务器端代码或者javascript代码有问题,而是通过Ajax下载文件的这种方式本来就是禁止的。出于安全因素的考虑,javascript是不能够保存文件到本地的,所以ajax考虑到了这点,只是接受xml,ajax,json格式的返回值,二进制的返回格式就会抛出这个异常。

因为response原因,一般请求浏览器是会处理服务器输出的response,例如生成png、文件下载等,然而ajax请求只是个“字符型”的请求,即请求的内容是以文本类型存放的。文件的下载是以二进制形式进行的,虽然可以读取到返回的response,但只是读取而已,是无法执行的,说白点就是js无法调用到浏览器的下载处理机制和程序。

如何解决这个问题?

1、用window.location.href = url的方式就:

有人会问,像上图这样的需求,在某个页面的时候点击下载链接,因为改变了window.location的值,岂不是当前页面就要被跳转了?事实是,我用的是Chrome浏览器,当点击那个link的时候,直接就弹出了这边的下个文件保存的对话框,而且页面地址栏也没有任何变化。这时候如果点击Save,文件就会保持,点击Cacel,操作就会取消,过程中当前页面会一直保持,不会跳转到其他页面。

如果一次执行多个location.href,实际后面的会将前面的替换掉,所以其实只能实现单个文件下载。

2、可以直接使用a标签实现浏览器文件下载:

<a href=”下载地址”>点击下载</a>

    var aLink = document.createElement('a');
    aLink.download = "文件名";
    aLink.href = "文件url地址";
    document.body.appendChild(aLink);
    aLink.click();
    document.body.removeChild(aLink);

3、POST方式浏览器下载:可以使用jquery创建表单并提交实现文件下载:

var form = $("<form>");
form.attr("style","display:none");
form.attr("target","_blank");
form.attr("method","post");
form.attr("action",rootPath + "T_academic_essay/DownloadZipFile.do");
var input1 = $("<input>");
input1.attr("type","hidden");
input1.attr("name","strZipPath");
input1.attr("value",strZipPath);
$("body").append(form);
form.append(input1);
form.submit();
form.remove();

或 

<button class="btn btn-default" id="download">下载</button>
function postExcelFile(params, url) {
    //params是post请求需要的参数,url是请求url地址
    const form = document.createElement("form");
    form.style.display = "none";
    form.action = url;
    form.method = "post";
    document.body.appendChild(form);
    // 动态创建input并给value赋值
    for (const key in params) {
        const input = document.createElement("input");
        input.type = "hidden";
        input.name = key;
        input.value = params[key];
        form.appendChild(input);
    }

    form.submit();
    form.remove();
}

$("#download").click(() => {
    postExcelFile({ currentPage: 2, pageSize: 20 },
        'url/xxxxxxx/');
})

 4、get方式浏览器下载:使用隐藏iframe或新窗体解决:

export const downloadFile = (url) => {
  const iframe = document.createElement("iframe");
  iframe.style.display = "none"; // 防止影响页面
  iframe.style.height = 0; // 防止影响页面
  iframe.src = url; 
  document.body.appendChild(iframe); // 这一行必须,iframe挂在到dom树上才会发请求
  // 5分钟之后删除(onload方法对于下载链接不起作用,就先抠脚一下吧)
  setTimeout(()=>{
    iframe.remove();
  }, 5 * 60 * 1000);
}

这个可以实现一次下载多个文件。

### Redis 分布式锁Redisson 的实现原理 #### 1. **Redis 分布式锁** Redis 分布式锁的核心思想是通过 `SETNX` 和 `EXPIRE` 来实现加操作。在早期版本(Redis 2.6.12之前),由于 `SETNX` 不支持设置过期时间,因此需要分两步完成:先调用 `SETNX` 创建键值对表示定状态,再调用 `EXPIRE` 设置过期时间以防止死[^3]。 然而这种两步操作无法保证原子性,可能会因网络延迟或其他异常导致未成功创建却设置了过期时间。为此,Redis 2.6.12引入了新的命令 `SET key value NX PX milliseconds`,其中 `NX` 表示只有当键不存在时才执行设置操作,`PX` 则用于指定毫秒级的过期时间。这种方式能够在一个命令内完成加并设定超时,从而有效解决了上述问题。 #### 2. **Redisson 实现分布式锁** Redisson 是基于 Redis 开发的一个 Java 客户端库,它不仅实现了更高级别的抽象接口,还提供了多种类型的分布式对象和服务功能。对于分布式锁而言,Redisson 提供了一种更加健壮可靠的解决方案——`RLock` 接口及其子类实例化方式。 - **加逻辑** Redisson 使用 Lua 脚本来确保整个加过程具有原子性。该脚本会检查目标资源是否已被占用;如果未被占用,则尝试获取并将当前线程 ID 记录下来作为持有者的唯一标识符[^1]。 - **续命机制** 当某个客户端成功获得之后,Redisson 会在后台启动一个定时器任务定期向服务器发送续约请求延长的有效期限,直到显式解为止。此设计可以避免因长时间运行的任务而导致提前失效的情况发生[^2]。 - **自旋重试策略** 如果初次未能取得所需资源,则按照预定义间隔不断重复尝试直至达到最大等待时限或最终放弃争夺控制权。 - **公平性和可靠性保障措施** 在某些特殊情况下(比如网络分区), 可能会出现部分节点认为自己已经拿到了全局唯一的,但实际上其他地方也有竞争者存在的情形下, redisson 还特别考虑到了这一点并通过内部复杂的协调算法尽可能减少冲突概率[^4]. #### 性能对比分析 | 特性 | Redis 原生分布 | Redisson | |-------------------------|------------------------------------------|----------------------------------| | 加效率 | 较高 | 略低 | | 安全性 | 存在网络抖动等问题 | 更安全可靠 | | 功能扩展能力 | 单纯提供基础加解鎖功能 | 支持更多特性如自动续租、可重入等 | | 易用程度 | 需要开发者手动处理很多细节 | API 封装良好易于集成 | 从表中可以看出虽然原生态方法简单高效但在实际应用过程中往往面临诸多挑战;而借助第三方工具包则可以在一定程度上弥补这些不足之处. ```java // 示例代码展示如何利用Redisson进行分布式锁管理 import org.redisson.api.RLock; import org.redisson.api.RedissonClient; public class DistributedLockExample { private final RedissonClient redissonClient; public void acquireAndReleaseLock(String lockName) throws InterruptedException{ RLock lock = redissonClient.getLock(lockName); try { boolean isLocked = lock.tryLock(10, TimeUnit.SECONDS); // 尝试获取最长等待时间为10秒 if(isLocked){ System.out.println(Thread.currentThread().getName()+" acquired the lock."); Thread.sleep(5000L); // Simulate some work }else{ System.err.println("Failed to get lock after waiting..."); } } finally { if(lock.isHeldByCurrentThread()){ lock.unlock(); System.out.println(Thread.currentThread().getName()+ " released the lock."); } } } } ``` ###
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值