Hello, I’m Kunge

Today to share two useful browser behavior do not agree with expected phenomenon, these two problems is not what problem, but in the work found that many people were puzzled, in my mind at least three colleagues in the group asked such a question, last week and colleagues trapped by this phenomenon, so I think this should be a common problem, share with everyone here, I hope it will be helpful to you. Welcome to follow the public account “Code Sea” and make progress together

Symptom 1. The file cannot be downloaded after you click the button

Feedback from colleagues at the front end: when I clicked the “download product picture” button in the browser, it could not be downloaded (I expected to download the ZIP file).

But if you enter the download address in the browser address bar and you can download it directly from the browser, why?

We can open the debugging tool “Network Section”, then click on the above “download product image”, first to see whether the network request is normal.

Content-type = content-disposition = Content-type = content-type = content-type = content-type = content-type = content-type = content-type = content-type = content-type = content-type = content-type = content-type = content-type = content-type = content-type

Content-type: Application/octet-Stream tells the client that this is a binary file, content-Disposition tells the client that this is an attachment that needs to be downloaded and tells the browser the default file name for the attachment.

If the response body of this request is the same as the application/octet-stream of step 1:

As you can see, response is a bunch of garbled code, which is the binary stream representation of the file, so there is no problem from the request, the file is returned normally, but why the file is not downloaded, where is the downloaded file? Pay attention to the red box **XHR ** in the figure above. Its full name is XMLHttpRequest, and it’s a representation of an Ajax request.

Ajax itself cannot trigger the browser’s download function, and its response will be processed by JavaScript. After the download is completed with Ajax, the response is stored in the memory in the form of a string, so it can’t be downloaded with Ajax? No, let’s see why the browser can download it

We found that using the browser GET request (mainly loaded by frame and triggered by a tag click) or POST request (in the form of form) can download the file, because this is a built-in event of the browser, the downloaded response will be handled by the browser itself. If the browser recognizes binary stream data, it will download. If it recognizes files that can be opened, such as XML, image, etc., it will not download, and will exist in the style of preview.

So why can’t Ajax download files by default? This is restricted by browser security policies. If Ajax can download files, that means Ajax can interact directly with the disk, which is a serious security risk.

According to the above analysis, we have the idea of using Ajax to download the file. Since the click event of using a tag (or frame) can trigger the built-in download behavior of the browser, then after we download the response with Ajax, This triggers the browser’s built-in download event, and you can download the file. However, you need to add a download attribute to the created a tag, such as download.

What’s the use of the Download property? For files that the browser can open, such as HTML, XML, etc., if you don’t download, clicking on the A TAB will not download, but open it. (Note that the Download property is currently only compatible with Firefox and Google.)

The following is an example of code to execute a downloaded file using Ajax:

const filename = response.headers['content-disposition'].match(
      /filename=(.*)/) [1]
// First create a Blob object (a file-like object representing immutable, raw data)
const blob = new Blob([response.data], {type: 'application/zip'});
if (typeof window.navigator.msSaveBlob ! = ='undefined') {
    / / compatible with Internet explorer, the window. The navigator. MsSaveBlob: save the file in local way
    window.navigator.msSaveBlob(blob, decodeURI(filename))
} else {
    let elink = document.createElement("a"); // Create a  tag
    elink.style.display = "none"; // Hide the tag
    elink.href = window.URL.createObjectURL(blob); // Configure href to point to the memory address of the local file
    elink.download = filename;
    elink.click();
    URL.revokeObjectURL(elink.href); // Release the URL object
    document.body.removeChild(elink); // Remove the  tag
}
Copy the code

Phenomenon two, in the browser input image link want to preview, the result turns into download image

This problem is actually based on the above analysis, I believe you can easily guess the cause, let’s first grab a bag to have a look:

As you can see, the content-type returned is octet-stream. As mentioned above, it refers to the binary stream of any Type. Generally, downloaded files return this Type. Because the content-type it returns is image/ PNG or image/ JPEG, the browser can recognize the open file directly, so no download event is performed

conclusion

The above two problems need us to have a certain understanding of the working mechanism of the browser and THE HTTP protocol, so the foundation is really very important ah, otherwise it is likely that you check for a long time also don’t know how to start, but if you know these principles, grab a packet analysis of their contence-type, instantly suddenly bright! In addition, understanding the HTTP protocol and how browsers work can help you quickly locate problems.

For example, in the solution above we get the name of the file with content-disposition

const filename = response.headers['content-disposition'].match(
      /filename=(.*)/) [1]
Copy the code

Response. Headers [‘content-disposition’] is null. Content-disposition [‘content-disposition’] is null

Why don’t you get Content-Disposition in the Reponse header?

A check found that the original or HTTP protocol problem

By default, only seven simple Response headers can be exposed to the outside world:

Cache-Control
Content-Language
Content-Length
Content-Type
Expires
Last-Modified
Pragma
Copy the code

By exposing to the outside, I mean making them accessible to clients (such as Chrome), both in the Network and in code.

Content-disposition is not one of them, so even if the server does include it in the protocol packet, here it is

response.setHeader("content-disposition"."attachment; filename=" + filename);
Copy the code

But because it is not “exposed” to the outside, the client “sees, does not eat”.

Access-control-exposure-headers is the “Expose” switch that lists which Headers can be exposed as part of the response.

If you want the client to be able to Access other Headers, the server should not only add the Headers to the header, but also list them in the access-control-exposion-headers, as follows:

response.setHeader("Access-Control-Expose-Headers"."Content-Disposition");
response.setHeader("content-disposition"."attachment; filename=" + filename);
Copy the code

So it should have content-disposition in the JS response header.

Reference: access-control-exposure-headers: 1il58.cn/AptUz