3

I learned that the body of a response of an HTTP message can be an arbitrary sequence of bytes so it doesn't need to be text/string. I guess that means when we need to transfer any binary files e.g. images / audio files over HTTP and we don't necessarily need to encode them into text/string using methods like base64, instead, we can just transfer them directly (of course with some compression e.g. gzip)

My first question is, I have not encountered any situations where I need to get actual images or other files via an API call e.g. a REST API call, instead normally we just get the URL to where the image is hosted and we use that URL to display the image in the web page and that seems to be more efficient to me. So I wonder in what circumstances we would have to transfer the actual image file over HTTP?

My second question is, if we are transferring the actual binary data over HTTP, I was looking at this and this and I found that there seems to be two ways to handle the response: 1. read the response as blob by response.blob() 2. read the response as arraybuffer by response.arrayBuffer(). I don't know what the differences are here. What determines whether or not we need to use blob or arraybuffer? Please refer to the below code snippet.

fetch(request, options).then((response) => {
  response.arrayBuffer().then((buffer) => {
  });
});
//  differences?
fetch(request, options)..then(response => response.blob())
  .then(images => {
})

My last question is, after we get the binary data either via response.blob() or response.arrayBuffer(), it seems like we still need to convert the binary data into string using window.btoa i.e. base64 encoding and turn that into URL and then assign it to a img element's src attribute. I guess I don't understand why we would still have to convert the binary data into URL when we already have the data. Is that the only way to display an image in JavaScript, i.e. using src attribute on img tag? Also, according this this and this , I found there seems to be 2 ways to get the URL to a file: 1. converting the buffer to string using btoa and then use data: URL with that string 2. Use URL.createObjectURL directly. Again, I wonder what the differences are here about these two different methods.

Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122
Joji
  • 4,703
  • 7
  • 41
  • 86
  • 1
    It's a good idea to ask 1 question at a time on stackoverflow. There's good research here, but it's hard to give 1 right answer to a dozen of questions. – Evert Mar 06 '21 at 22:18
  • 2
    I recommend you spend a great deal of time looking into MDN as a resource. For instance, Q3 is answered plainly [here](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/btoa) and [here](https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL) - refer specifically to what they return and you'll have your answer. Look [here](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Sending_and_Receiving_Binary_Data) for Q2 info. Finally, Q1 is opinion based. Good luck to you. – Randy Casburn Mar 06 '21 at 22:27
  • 1
    You would almost never have to transfer an image or any other binary file over HTTP and handle that transfer's request or response in JavaScript, in the browser. The use cases for such behavior are when you want to provide some kind of client-side value. For instance, perhaps you want to show a preview of an image as you've received it. Or you want to validate its size meets certain criteria. Once you have the binary data, there is no reason to convert it to a string: [`createObjectURL`](https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL) takes a File or Blob. – Heretic Monkey Mar 06 '21 at 23:15
  • @RandyCasburn Hey thanks for the answer. Not sure about what you meant here, "Finally, Q1 is opinion based." so you meant I should not ask opinion-based questions on here? I thought people have experienced dealing with those could share their insight. – Joji Mar 06 '21 at 23:44
  • Right, open ended questions such as "_what circumstances_" don't lead to definitive answers. We can't anticipate every circumstance (use case) in which an image may be need to be transfered. So questions like that lead to long discussions or many, many answers. But none provide a technical solution. – Randy Casburn Mar 07 '21 at 03:22
  • @RandyCasburn I read those MDN pages _before_ I asked the question. I understand what those two APIs are doing specifically but my question was about what determines whether or not we need to use blob or arraybuffer because it seems like in both cases they are binary data transmitted over HTTP. How would I know if I should invoke either one function on the response data? Btw those MDN doesn't really explain my first question. – Joji Mar 07 '21 at 05:35
  • @HereticMonkey "Once you have the binary data, there is no reason to convert it to a string" yea that was my thought too but from those two code snippets I mentioned in the question they are indeed encoding the binary data into URLs. MDN suggests that it seems like the only way to display an image on the web page is via URL, for whatever reason you cannot get the original URL, then you have to convert that binary physical imagines to a URL and display it. I am not sure if I understood it correctly. – Joji Mar 07 '21 at 05:37
  • I think there may be a single truth that you have not grasped about both HTML and HTTP that has to do with the second letter in each (T). HTML and HTTP are text based. The markup language only understands characters and the protocol only transfers text. Yes, HTTP only transfers text. In fact, the three transfer-encoding mechanisms employed by HTTP convert the data to be sent into ASCII character codes. When the browser receives the data it is decoded back into its initial form. – Randy Casburn Mar 07 '21 at 14:20
  • If it was a binary file, thee is no way for you to jam that binary file into a DOM that only knows how to construct content from text. To demonstrate what I mean about HTML and the DOM, drag-n-drop an image file onto a browser window. Once the image is shown, open the dev tools and look at the elements tab. You will find a full HTML document with an `` element. Finally, if you need a URL use an API that gives you a URL. Otherwise you find yourself creating a string and then crafting a URL from that. – Randy Casburn Mar 07 '21 at 14:23
  • @RandyCasburn hey thanks for the answer. I agree that it was a binary file, there is no way for you to jam that binary file into a DOM. We need to construct some sort of local URL. But I am pretty sure, 100% HTTP can transfer non text data as well. Sure the HTTP header can only be text. but the body of a request or response is an arbitrary sequence of bytes. Take a look at this https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types#applicationoctet-stream – Joji Mar 08 '21 at 07:26
  • HTTP transfers an arbitrary sequence of _encoded_ bytes as an RFC 822 text message. You are welcome to discover that the respective RFCs on your own. Here's a start: https://tools.ietf.org/html/rfc2616#section-4 – Randy Casburn Mar 08 '21 at 13:23
  • 1
    @RandyCasburn hi thanks for the reply. Indeed it says " an arbitrary sequence of encoded bytes ". I am a little confused. So if we set the `content-type` to `application/octet-stream`, it is still going to be transferred as non-binary, text data? – Joji Mar 09 '21 at 20:21
  • 1
    Yes, as my comment said above, it will be character encoded at the source and decoded within the browser. But this is all transparent to us as front end devs. Other than for academic reasons, we just shouldn't care too much about this detail. But now you know. – Randy Casburn Mar 09 '21 at 22:53
  • @RandyCasburn the HTTP headers are indeed content encoded, but I've sent body [as binary](https://tools.ietf.org/html/rfc2616#section-2.2) many times for custom data. It is traditionally using ASCII-encodable MIME types, but the body can be pure binary written to the stream. Is it converting in some way I'm not aware? – Matt E. Mar 22 '21 at 15:22
  • @MattE. - You should not confound _content encoding_ with _transfer encoding_ - they are two separate things. The content surely can be binary, but is indeed transferred as 7 or 8 bit text that is compliant with the MIME standards similar to those found in RFC-822 - a text transfer specification. – Randy Casburn Mar 22 '21 at 15:49
  • @MattE. - Open this link and search for "binary" - within minutes you'll discover that what I am saying is true. Hope this is helpful: https://tools.ietf.org/html/rfc2045 – Randy Casburn Mar 22 '21 at 15:59

2 Answers2

2

For Question 1: When you point the src of an img to a URL in a browser, it will do a 'raw' image GET using HTTP. The content type will be the MIME type of the file and the body will be binary in the file's format. Since MIME is used for SMTP which limits the transfer encoding to 7-bit, many MIME formats are converted to base64 for its portability across many message handlers.

HTTP headers and most content encodings are built over ASCII, a 7-bit subset of text that works with SMTP (email); however, the body of an HTTP response can be pure binary. The browser or the XHR (XmHttpRequest) provides an HTTP client that understands and processes the HTTP headers to give access to the body. It is up to the content producer to ensure the binary matches the format the consumer needs, and it doesn't have to conform to a known file type.

In most cases, the direct transfer without changing the content-type works better, and there are standards so any browser knows how to process a common file. MIME types have expanded to cover all publicly known files but we can always send our own format or user application/octet for binary. In some cases however, when we want error handling or we need to send an 'envelope' of data without an existing file type, we can contain it in an encoding like JSON/XML or multipart form data.

For Question 2: The difference between arrayBuffer and blob have been defined in the referenced answer. For the purpose of reading bytes, they are very similar except that blob is read-only.

For Question 3: The browser can already read base64 encoded binary in an image, so it is a simple way to set the content. It is not converting the binary into a string as much as converting base2 to base64 within an ASCII text readable space--base64 is more of a compression than conversion.

If this seems excessive, it's because it is. If you don't need to download and render images synchronously, don't. Its much more effective to assign the link to the img and let them load in parallel then to parse a huge object and assign its data in series. That level of complexity is only needed (if at all) for saving uploaded data that needs to be processed as a unit.

Community
  • 1
  • 1
Matt E.
  • 950
  • 1
  • 6
  • 15
0

Answer: Question #1

An example of needing the raw data of an image/video file is actually editing that file. There are lots of libraries that can handle this for you like web-based versions of FFmpeg. Personally I rarely edit raw data without help of an external library, but I have done it before when its necessary. I was trying to "fake" a WebM/MKV file's duration.

Here is the process:

Step 1. Upload a file through HTML's <input type='file'> or through JavaScript's showOpenFilePicker()
Step 2. Read that file as an ArrayBuffer then convert it to a Uint8Array
Step 3. Change the values needed according to the Matroska's Element Specification
Step 4. Create an ArrayBuffer from the edited Uint8Array
Step 5. Create a new File from the ArrayBuffer and save it

Answer: Question #2

The method fetch returns a response. With the response, you can do a variety of things:

  • Response.prototype.text()
  • Response.prototype.blob()
  • Response.prototype.arrayBuffer()
  • Response.prototype.json()
  • Response.prototype.formData()

There are many more things to do with a response, but those are the interface methods. A way to think of an arrayBuffer is a Blob's contents. This can be turned into all sorts of other ways to view a file's data.

Answer: Question #3

If you wan't to access the text use Response.prototype.text(). For example with async:

(async function() {
    let res = await fetch('https://www.example.com');
    let text = await res.text();
    //Do whatever with the variable text
})();

or with Promise.prototype.then

fetch('https://www.example.com').then(res=>res.text()).then(text=>{
    //Do whatever with the variable text
});

or turning the raw data into text with TextEncoder

(async function() {
    let res = await fetch('https://www.example.com');
    let buffer = await res.arrayBuffer();
    let uint = new Uint8Array(buffer);
    let decoder = new TextDecoder();
    let decodedText = decoder.decode(uint);
    //decodedText is equal to res.text()
})()
quicVO
  • 778
  • 4
  • 13