Does Safari on iOS refuse to download `application/octet-stream` content? - ios

I have a Web server written on top of your standard http module in Node.js. It's been working like a clock for me for serving all kinds of HTML, JavaScript, CSS, and asset content -- making up the bulk of my web pages, naturally.
When the service is requested a URL that maps to a file with an unknown extension (there is no MIME sniffing going on), it simply serves content using chunked transfer encoding with the header Content-Type: application/octet-stream, which has been working wonderfully as well -- I occasionally host files of all kinds on the server and am able to download these without any interference from the user agent, as I'd expect from application/octet-stream handling.
Today, however, I tried to download on a Safari mobile browser running on iOS 9.3.5 on an iPhone 4S, and that thing just plain refused to download from a valid URL, alerting me with:
Download failed. Safari cannot download this file.
I've since tried to add Content-Disposition: attachment and also Content-Disposition: attachment; filename="foobar" for good measure, but it still refuses.
This has been the case with a file called random which is filled with 1000 random bytes. My host headers are (Content-Disposition is optional, as said above):
HTTP/1.1 200 OK
Host: example.com
Content-Type: application/octet-stream
Content-Disposition: attachment; filename="foobar"
Date: Mon, 01 May 2017 13:48:37 GMT
Connection: keep-alive
Transfer-Encoding: chunked
For the record, Android 6.x phone also behaves as expected (in addition to Firefox 53 on my Windows 10 x86_64 host), downloading from the URL without a hitch.
What's going on with Safari here?

Yes, the only thing you can download with Safari on iOS is image. This is because iOS does not expose file structure to user (download file to somewhere user can never see does not make any sense), it has nothing to do with anti web attack. Browser on Android can download arbitrary files because Android file structure is visible to user.
Here is a previous discussion on Apple community: https://discussions.apple.com/thread/3697948?start=0&tstart=0
The ONLY thing you can save to an iPhone, from Safari, is a pic. Nothing else, and no downloads.
It is possible that iOS supported video files can be downloaded in future, but it is unlikely for arbitrary files with application/octet-stream MIME type.

After I have spent so much time trying to find the solution, I came with this solution:
Replace the octet-stream with pdf.
<?php
$file = 'data/report/foobar.pdf';
header('Content-Description: File Transfer');
header('Content-Type: application/pdf');
header('Content-Disposition: attachment; filename="'.$file.'"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: '.filesize($file));
readfile($file);
?>

Related

HTML5 web app not caching in iOS Safari for offline use

I am developing a HTML5 web app for use offline on an iPad2 using mobile Safari and the "Add to home screen" feature. I am able to achieve offline caching using a cache.manifest file in desktop Chrome but cannot make it work in iOS mobile Safari.
The app runs smooth on the iPad while online, but once I go offline I get these error messages: "MyApp could not be opened because it is not connected to the internet" (in "added to home screen" view on an iPad) and "Safari cannot open the page because it is not connected to the internet" (in safari-view on that same iPad).
I have read hundreds of troubleshooting / question pages and manifest tutorials on the Net trying to resolve this issue and none of the suggestions work. After reading so much about this capability it should be very easy to implement and yet here I am.
Here is a summary of what I have done / tried / used so far without success. I have tried all of the below using both cache.manifest and manifest.appcache variations without success but for simplicity I will only document the cache.manifest case:
I am developing and testing using latest Xampp Apache for Windows server locally installed on Win10 x64
The target device is an iPad2 running iOS version 8.4 and mobile safari version 8. My full user agent string is:
Mozilla/5.0 (iPad; CPU OS 8_4 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12H143 Safari/600.1.4
In Xampp I have updated the httpd.conf file to include the correct MIME types for .manifest
AddType text/cache-manifest .manifest
In Xampp I have updated the mime.types file under xampp\apache\conf\ to include the correct MIME types for .manifest
text/cache-manifest manifest
In Xampp, as my web app uses ttf, woff, ico, png, jpg, js, mp3 and css files, I have verified the mime.types file under xampp\apache\conf\ to ensure it includes the MIME types for:
application/x-font-ttf ttf ttc
application/x-font-woff woff
image/x-icon ico
image/png png
image/jpeg jpeg jpg jpe
application/javascript js
audio/mpeg mpga mp2 mp2a mp3 m2a m3a
I have placed a .htaccess file in the web apps root public HTML directory for the correct MIME types for .manifest
AddType text/cache-manifest .manifest*
I have included the manifest attribute in the HTML element of the index page:
<!DOCTYPE html>
<html lang="en" manifest="cache.manifest">
<head>
I've tried removing this line from the declaring index.html but it did not work:
<meta name="apple-mobile-web-app-capable" content="yes">
I've allowed plenty of time for the app to cache in Safari before switching to Airplane mode and refreshing. I am using a Windows machine so cannot use Web Inspector to debug. I used Jonathan Stark's Debugging Script and JSConsole to try and debug but it doesn't really give much useful information except that it is uncached which I know because it isn't working.
I have created a cache.manifest file and placed it in the web apps root public HTML directory. I have included the advice of other solutions, many of which where derived from other stackoverflow questions, including:
Primarily I've stuck with the cache.manifest name as multiple sources have advised Safari mobile will igrnore everything else
Not including the index.html file which references the .manifest
Listing all resources under the CACHE section
Including the * after NETWORK:
Including all section headers even if not used
Used only relative URI's
The manifest file contents are relative to the manifest file (it is in the web apps root directory with index.html)
The manifest file is being served from the same origin as the host
Ensured all files are available to avoid errors and dropping the .manifest. As I mentioned offline caching is working in desktop Chrome which validates the manifest's contents
The manifest file does not list the manifest file
The content of the manifest is:
CACHE MANIFEST
# ver 0.0.8
CACHE:
data/apple-touch-icon.png
data/favicon.ico
data/fnt0.ttf
data/fnt0.woff
data/fnt1.ttf
data/fnt1.woff
data/fnt2.ttf
data/fnt2.woff
data/fnt3.ttf
data/fnt3.woff
data/html5.png
data/html5-unsupported.html
data/img0.jpg
data/img1.png
data/img10.jpg
data/img11.jpg
data/img12.png
data/img13.png
data/img14.png
data/img15.png
data/img16.jpg
data/img17.png
data/img18.png
data/img19.png
data/img2.png
data/img20.png
data/img21.png
data/img22.png
data/img23.png
data/img24.png
data/img25.png
data/img26.png
data/img27.png
data/img28.png
data/img29.png
data/img3.png
data/img30.png
data/img31.png
data/img4.png
data/img5.png
data/img6.png
data/img7.png
data/img8.png
data/img9.png
data/player.js
data/slide1.css
data/slide1.js
data/slide10.css
data/slide10.js
data/slide11.css
data/slide11.js
data/slide12.css
data/slide12.js
data/slide13.css
data/slide13.js
data/slide14.css
data/slide14.js
data/slide15.css
data/slide15.js
data/slide16.css
data/slide16.js
data/slide17.css
data/slide17.js
data/slide18.css
data/slide18.js
data/slide2.css
data/slide2.js
data/slide3.css
data/slide3.js
data/slide4.css
data/slide4.js
data/slide5.css
data/slide5.js
data/slide6.css
data/slide6.js
data/slide7.css
data/slide7.js
data/slide8.css
data/slide8.js
data/slide9.css
data/slide9.js
data/sound1.mp3
NETWORK:
*
FALLBACK:
I would really appreciate some fresh eyes on this issue, I just can't see where the problem could be.
Can you try to decrease the size of cached files? In my case it helps, but not solved all the problems) Cached files size was at least 30 Mb, after weight loss they become <1 Mb and AppCache finally start working.

Youtube-API: Upload binary captions file (ebu-stl)

Youtube supports some binary caption file formats, such as ebu-stl.
I've got an *.stl file that uploads and processes just fine when I upload it via the web interface. But when I try to upload it via the API v2 with a POST request, it does not seem to recognize the file format properly. The POST request looks like this:
POST /feeds/api/videos/VIDEO_ID/captions HTTP/1.1
Host: gdata.youtube.com
Content-Type: application/vnd.youtube.timedtext; charset=UTF-8
Content-Language: en
Slug: Title of caption track
Authorization: Bearer ACCESS_TOKEN
GData-Version: 2
X-GData-Key: key=DEVELOPER_KEY
<Caption File Data>
This is how the *.stl file uploaded via the API looks on the website. There should be several lines with readable text.
Everything works fine when I upload a utf8 plain text subtitle file (eg *.vtt) with the same code. Furthermore, if I upload the stl file once via the web interface and once via the API, and retrieve both files through the API afterwards, they are byte-identical.
It looks like an encoding issue to me- youtube receives the file correctly, but probably parses the entire binary file as UTF-8. However, youtube responds with a 4xx error when I ommit either charset= or Content-Language.
Is it possible to upload binary caption files? I would also appreciate confirmation in case it is not possible.
Minimalistic stl file if you want to try it yourself (hex dump). Read it as (ruby):
stl = hex.chars.each_slice(2).map{|x|x.join.to_i(16)}.pack('C*')
API v2 is now officially deprecated. As of April 2, the API v3 now officially support captions. Binary caption files are supported, too.
The documentation is lacking some details as to the raw HTTP requests. You could try using one of their libraries.
I used their python script and took a look at the request it made. For reference, here's a minimal working HTTP request for uploading a binary subtitle/captions file.
The docs aren't mentioning this parameter, but setting uploadType=multipart is important, the API returns an error otherwise.
POST /upload/youtube/v3/captions?uploadType=multipart&part=id,snippet&sync=false HTTP/1.1
Host: www.googleapis.com
Accept: */*
Authorization: Bearer {AUTH_TOKEN}
X-GData-Key: key={YOUR_KEY}
Content-Type: multipart/related; boundary================83250640405719953005==
Content-Length: 7147
Expect: 100-continue
--===============83250640405719953005==
Content-Type: application/json
MIME-Version: 1.0
{"snippet":{"videoId":"Agn_uesF248","language":"en","name":"Subtitle Test 2","isDraft":false}}
--===============83250640405719953005==
MIME-Version: 1.0
Content-Type: application/sla
Content-Transfer-Encoding: binary
{BINARY_CAPTION_FILE_DATA}
--===============83250640405719953005==--
application/sla are ebu-stl caption files. Use text/plain for plain text subtitles (.srt, .vtt, .ass, &c.).
Replace {AUTH_TOKEN} and {YOUR_KEY} with the proper authorization credentials for the scope https://www.googleapis.com/auth/youtube.force-ssl; and {BINARY_CAPTION_FILE_DATA} with the file you wish to upload.

Force MP3 link to download instead of stream but keep HTML5 audio

So apparently the way to force an MP3 to download instead of play in the browser is to set the MIME type as file and/or set the Content-Disposition response header in the .htaccess.
What is the difference between these two methods and is it better to use one or the other, or both?
Also, will doing either of these break HTML5's handling of the <audio> tag when using an MP3 file as the source?
1. Use headers correctly
This is a very widespread problem and unfortunately even the PHP manual is plagued with errors. Developers usually say “this works for me” and they copy stuff they don’t fully understand.
First of all, I notice the use of headers like Content-Description and Content-Transfer-Encoding. There is no such thing in HTTP. Don’t believe me? Have a look at RFC2616, they specifically state “HTTP, unlike MIME, does not use Content-Transfer-Encoding, and does use Transfer-Encoding and Content-Encoding“. You may add those headers if you want, but they do absolutely nothing. Sadly, this wrong example is present even in the PHP manual.
Second, regarding the MIME-type, I often see things like Content-Type: application/force-download. There’s no such thing and Content-Type: application/octet-stream (RFC1521) would work just as fine (or maybe application/x-msdownload if it’s an exe/dll). If you’re thinking about Internet Explorer, it’s even better to specify it clearly rather than force it to “sniff” the content. See MIME Type Detection in Internet Explorer for details.
Even worse, I see these kinds of statements:
header("Content-Type: application/force-download");
header("Content-Type: application/octet-stream");
header("Content-Type: application/download");
The author must have been really frustrated and added three Content-Type headers. The only problem is, as specified in the header() manual entry, “The optional replace parameter indicates whether the header should replace a previous similar header, or add a second header of the same type. By default it will replace“. So unless you specify header("Content-Type: some-value", FALSE), the new Content-Type header will replace the old one.
2. Forcing download and Internet Explorer bugs
What would it be like to not having to worry about old versions of Internet Explorer? A better world, that’s for sure.
To force a file to download, the correct way is:
header("Content-Disposition: attachment; filename=\"$file_name\"");
Note: the quotes in the filename are required in case the file may contain spaces.
The code above will fail in IE6 unless the following are added:
header("Pragma: public");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
Now, the use of Cache-Control is wrong in this case, especially to both values set to zero, according to Microsoft, but it works in IE6 and IE7 and later ignores it so no harm done.
If you still get strange results when downloading (especially in IE), make sure that the PHP output compression is disabled, as well as any server compression (sometimes the server inadvertently applies compression on the output produced by the PHP script).
Look at this,
.mp3 audio/mpeg3
.mp3 audio/x-mpeg-3
.mp3 video/mpeg
.mp3 video/x-mpeg
See this link for more info.
Using Content-Disposition: attachment... forces a download box to appear instead of having to right click -> save target as.

playing a WAV file on iOS Safari

I'm struggling to get a WAV file to play on a HTML page as either an HTML5 audio tag or via a regular downloadable link in mobile Safari (iPad/iPhone):
<audio controls src="audio-pcm_s16le-8k.wav"/></audio>
audio
The HTML5 audio object just shows Cannot play audio file in it, and when I click to download the anchor referenced one I get a black page with a "can't play this circle" in the middle. I've tried various frequencies (8000, 11025, 16000, 32000, 44100), various encodings (mu-law, Linear Signed 16-bit LE and BE), various containers (.wav, .caf, .aiff), and various audio conversion programs (Audacity, ffmpeg, and Apple's own afconvert)... I can't get audio to play (unless I make it MP3 -- and no, I can't just use MP3 or AAC, I need a "raw" format for reasons too long to get in to here).
I looked at the supported formats for iOS and it appears to support WAV... anyone got any experience with this issue? I'm on latest iOS 6.0.1
EDIT: The selected answerer got me to the issue, but the reason is in the comments of the answer. Bottom line is it requires HTML range headers for playing the files.
Well, you've certainly gotten me on the right track at least... it's apparently not the file at all... it's the web server. Same exact content from stockley works, but on my web server it doesn't. I'll have to look over the HTTP response in detail I suppose. My web server is returning Content-Type: audio/x-wav so it's not that...
Checking the headers for the test site I set up, you want to be returning Content-Type: text/html for the page containing the HTML data you need:
And Content-Type: audio/x-wav for the actual audio file (http://www.test.com/file.wav)
Through a run of trial and error I found that you need a content-range header for it to work. Here's an example of my headers that allows a wav file to play in Safari for iOS:
Content-Range: bytes XX-XX/XX
Content-Type: audio/wav
Content-Disposition: attachment; filename="whatever.WAV"
Content-Length: XX
Hope this helps!
It works great. I used Content-Range: bytes XX-XX/XX and Content-Length: XX

Automatic translation while uploading a document using Google documents list API does not work

I try to automatically translate a document using version 3 of Google Document List API.
The documentation just says to add a "sourceLanguage" and/or a "targetLanguage" parameter to the initial HTTP POST request.
So here is the request I'm sending :
POST /feeds/upload/create-session/default/private/full?sourceLanguage=fr&targetLanguage=en
Authorization: GoogleLogin auth=<authentication token>
GData-Version: 3.0
Content-Type: application/atom+xml
Content-Length: 163
X-Upload-Content-Type: text/plain
X-Upload-Content-Length: 50
<?xml version="1.0" encoding="UTF-8"?><entry xmlns="http://www.w3.org/2005/Atom" xmlns:docs="http://schemas.google.com/docs/2007"><title>french.txt</title></entry>
I tried with very simple .txt files as well as .doc (french to english).
The files are uploaded correctly to the service, but translation does not occur.
The translation using Tools->Translate menu on the uploaded documents works OK, so the "translation engine" is able to perform the translation I'm trying to do through the API.
Is the documentation wrong ? How does automatic translation work ?

Resources