网站推广初期目标,营销网络地图,轻量服务器wordpress,wordpress主题图片不居中背景及需求描述
背景
记录分享下近期遇到并解决的困扰了比较久的问题#xff1a;在不同系统微信生态发现同一个cos地址用window.open(url)打开在苹果和安卓设备的微信生态上表现不一致#xff1a;对于文档类型#xff0c;响应头Content-Type: application/pdf 在安卓微信上…背景及需求描述
背景
记录分享下近期遇到并解决的困扰了比较久的问题在不同系统微信生态发现同一个cos地址用window.open(url)打开在苹果和安卓设备的微信生态上表现不一致对于文档类型响应头Content-Type: application/pdf 在安卓微信上唤起浏览器下载弹窗在ios微信、 safari等浏览器中则直接打开了该文档[出现这种差异的原因有懂的大佬欢迎指教~]。
要在ios设备中实现下载需配置响应头Content-Type: application/octet-stream,但是该响应头在安卓微信内变成无法下载提示“可在浏览器打开此网页来下载文件” 。
目前生产环境文档资源配置响应头Content-Type: application/octet-stream(为解决ios设备无法下载pdf), 最后是联系云厂商根据设备信息定制响应头解决了问题。但是在不直接向用户暴露出资源链接的情况下前端其实也是可以通过“修改响应信息”达到相同效果的。
目标效果 ios微信 android微信 浏览器弹出自带的下载确认弹窗 基于现状我们的目的是如何修改实现安卓手机的微信中可以唤起跳转到浏览器的弹窗。
需求
我们知道 用window.open(url)访问资源其实就是浏览器向该url指向的服务器发了个get请求,我们能用这种方法下载文件是因为当使用window.open(url)时浏览器会新开一个窗口或标签页(没带_self的情况)并尝试对给定的url进行导航/渲染如果url指向的是个文件并且正确配置了响应头浏览器会尝试直接下载该文件。
上面说的响应头包括
Content-Type Content-Type 实体头部用于指示资源的 MIME 类型 media type 。在响应中Content-Type 标头告诉客户端实际返回的内容的内容类型。浏览器会在某些情况下进行 MIME 查找并不一定遵循此标题的值; 为了防止这种行为可以将标题 X-Content-Type-Options 设置为 nosniff。(引用自MDN) 这个响应头是用来告诉浏览器返回的内容是什么类型的。例如它可以是“text/html”表示内容是HTML文档或者“image/jpeg”表示内容是一张JPEG图片
Content-Type: application/octet-stream告诉浏览器服务器发送的响应内容是二进制格式的。它是一种通用的二进制文件类型可以用于任何类型的二进制文件包括文本文件、图片、音频、视频等。但是由于它没有特定的子类型因此它通常被视为应用程序文件的默认值但在实际应用中很少直接使用。 Content-Type: application/pdf则是一种确定的二进制文件类型用于指示服务器发送的响应内容是PDF格式的文件。当Content-Type设置为application/pdf时浏览器会知道响应的内容是PDF文件并采取相应的措施来正确地显示和渲染该文件看浏览器能力是解析还是下载如果浏览器有内置pdf解析器或有解析插件就会解析该文档然后渲染文档内容。
这个头部信息并不会直接决定内容是否被下载而是告诉浏览器这是哪一种媒体类型如何处理和显示这个内容取决于浏览器本身的处理程序。 总结这是个啥啥啥怎么处理你浏览器自己决定。
Content-Disposition 在常规的 HTTP 应答中Content-Disposition 响应标头指示回复的内容该以何种形式展示是以内联的形式即网页或者页面的一部分还是以附件的形式下载并保存到本地。(引用自MDN) 这个响应头告诉浏览器该如何处理返回的内容。 它可以被设置为“inline”或者“attachment”。一般情况下当设置为“inline”时内容通常会直接显示在浏览器窗口中例如图片或PDF文件。而当设置为“attachment”时浏览器通常会提示用户下载内容而不是直接在浏览器窗口中显示。
总结我要浏览器怎么怎么做。
也就是说上述两个响应头影响了不同系统和浏览器中访问文件地址的表现而我们在“背景”部分也介绍清楚了如何修改可以实现我们的需求接下来便是实践部分
显然我们直接用window.open(url)或者直接用下面提到的a标签的方法不能去拦截http请求/响应当我们主动用这个url发个get请求就可以对请求和响应进行拦截并做出相应的修改。下面我们拆解下任务
实现前端发送一个http请求并下载资源拦截并改写http的response Header 【生产环境服务器配置响应头是Content-Type: application/otect-stream,因此修改安卓设备的响应头为Content-Type: application/pdf即可】
发送http请求并下载资源
需要注意的是不管用哪一种请求库向资源发起请求这个动作本身并不会将文件下载到本地我们只是主动像服务器发了个http请求、获取到响应的数据。我们需要额外的处理程序下载文件——常见的方法是创建一个a标签设置其href属性为该资源url并设置download属性然后去触发它的点击事件。 设置了download属性的a标签click()事件被触发后会默认导航到href属性所指向的url也就是向url所指向的服务器发送请求。这是浏览器代理的用其内置的下载机制。
请求到数据后我们需要将返回的内容存放到一个容器里然后创建一个url指向这个容器这样我们需要用的时候就可以访问到而不用重复向服务器请求。下面我们将会用到js的Blob对象来充当这个容器将response的内容转换成二进制数据放到这个blob实例对象中并用URL.createObjectURL(blob)创建一个指向这个容器的变量(一个URL)在下载的时候直接用这个URL。
xhr方式
let url https://xxx?download_name%23%E6%AF%8F%E6%97%A5%E7%9C%8B%E7%82%B92022.01.01.pdf;fetch(url).then(response {return response.blob();}) .then(blob { // 处理二进制数据 const url URL.createObjectURL(blob); //创建指向Blob对象实例的url//下面是下载程序const link document.createElement(a); link.href url; link.setAttribute(download, file.pdf); // 下载文件的文件名 document.body.appendChild(link); link.click(); document.body.removeChild(link); }) .catch(error console.error(下载文件时发生错误:, error));axios方式 const instance axios.create();instance.get(url, { responseType: blob }) .then(function (response) { // 处理二进制数据 const blob response.data;const url window.URL.createObjectURL(blob); const link document.createElement(a); link.href url; link.setAttribute(download, file.pdf); // 设置文件名 document.body.appendChild(link); link.click(); setTimeout(() {link.remove()}, 0)}) .catch(function (error) { // 处理错误...});我们已经实现了第一步的目标并将该pdf下载到本地,下一步便是修改响应头。
修改响应头实现需求
我们知道前端请求库如axios有提供拦截器(interceptors)可以修改请求参数、请求标头、response的数据格式等。Fetch API也提供了修改请求头的方法
//修改请求头
let headers new Headers();
headers.append(Content-Type, image/jpeg);
headers.append(Another-Header, Another-Value);
fetch(url, { method: GET, headers: headers, body: data
})
经过多方实践发现哪怕用拦截器也只能修改响应体也就是返回的数据(如格式化一下、增加以下自定义属性等等更多应用敬请自行探索)并不能直接拦截并修改响应头对于额外设置的自定义response Header也不会生效
instance.interceptors.response.use( response { // 对响应数据做点什么 response.headers[content-type] application/pdf; response.headers[12-1] application/pdf; return response; }, error { return Promise.reject(error); }
); 当我们尝试求改请求头content-type的值时会出现错误TypeError: Failed to execute ‘set’ on ‘Headers’: Headers are immutable。 因为在 Fetch API和Axios中一旦请求被发送就不能更改请求头。 得看起来白忙活一场了改不了一点。。。 当我尝试了解js的Blob对象事情迎来了转机 Blob对象包含两个只读属性size和type这个type啊是个字符串表明该 Blob 对象所包含数据的 MIME 类型。如果类型未知则该值为空字符串。emmmmmm MIME类型。。。不正和我们要修改的Content-Type对应上了嘛愣着干啥开干 等等只读属性。。。 改不了改不了一点。。。 不死心又菜菜的我还是去问ai
复制对象好办法我们拿到了响应的数据搞个新的Blob对象至于MIME类型重设下触发下载的时候骗过浏览器不就好了嘛
const instance axios.create();
instance.get(url, { responseType: blob }) .then(function (response) { // 处理二进制数据 const blob response.data;let newBlob new Blob([blob], {type: application/pdf}); // 重点在这里在这里可对二进制数据进行处理比如创建一个可下载的链接 const url window.URL.createObjectURL(newBlob); //important!!!const link document.createElement(a); link.href url; link.setAttribute(download, file.pdf); // 设置文件名 document.body.appendChild(link); link.click(); setTimeout(() {link.remove();URL.revokeObjectURL(url); // 释放内存}, 0)
})
.catch(function (error) { // 处理错误 console.error(error);
});至此我们已经实现了目标需求在安卓微信上也可唤起跳转到浏览器的弹窗啦。 如前所述这种方法适用于不暴露cos链接的情况如果直接复制cos链接到安卓微信打开还是会弹出toast提示到浏览器去下载这时候就需要去修改服务器的响应头了。去掉响应头中Content-Disposition的Utf-8也可绕过看起来是不同浏览器接受对响应头中中文编码设置方式的差异造成的 详见 https://stackoverflow.com/questions/18050718/utf-8-encoding-name-in-downloaded-file 完结撒花如有谬误敬请指出~
关注我一起学习前端知识【后面会介绍同时下载大量级文件、大文件如何处理敬请期待~】