iOS Safari issue with Audio from Server - ios

So currently working on a project and experiencing a strange issue with the ios version of Safari involving the playback of an audio file from a server.
I'm currently facing the following issue:
Person comes onto the page which has a standard html5 audio tag, and a direct link to the audio file for downloading purposes.
Person tries to listen to audio from audio tag, content plays for x number of minutes, cuts off then repeats (the x number of minutes is NOT the length of the recording, and is not consistent).
Person tries direct link of recording, rather then downloading the recording, Safari appears to go to a new page and wraps the download url in a video element, and the same issue as step 2 occurs.
Now the audio file is served up via a java scriptlet, which serves the file with the following code snippet:
String fn = saveTo + file_name;
f = new File(fn);
String fname = f.getName();
String contentType = "audio/wav";
if(fname.endsWith("mp3")){
contentType = "audio/mp3";
}
response.setContentType(contentType);
response.setHeader("Content-Transfer-Encoding", "binary");
response.setHeader("Content-disposition", "attachment;filename="+f.getName());
response.setHeader("Content-Length", ""+f.length());
FileInputStream fin = null;
try{
fin = new FileInputStream(f.getCanonicalFile());
byte[] data = new byte[1024];
int x = 0;
while((x = fin.read(data, 0, 1024))>=0){
response.getOutputStream().write(data, 0, x);
Thread.sleep(1);
}
} finally {
if(fin != null) {
try{
fin.close();
}catch(Exception ex){}
}
}
Now I know the code isn't the best by any measure, it isn't my code, and we're obviously working on the assumption that the file is found.
I'm finding when debugging on the iPhone with debug mode on a mac, it doesn't seem to show a return status code. It shows no response headers but it obviously must be receiving something. The server log seems to think its returning a status 200, this showing in Chrome and Firefox.
The code above appears to work fine with Chrome, and Firefox, but not Safari.
The only thing I am guessing is it has something to do with how the file is being pushed to the output stream that Safari isn't liking, or maybe its getting confused and should have a different status code. I've been banging my head against this for a good few days, and reading as much as I can about Safari, though most of the documentation I'm finding is on its "unique" implementation of web audio, and the use of a single channel which seems to be irrelevant to this.
Any Help would be appreciated.

I experienced the same issue with Safari on iOS, and after a lot of debugging, I found the issue was related to the combination of headers applied to the response.
My application is C#-based, but this solution should be platform independent (because as previously stated, it is a response header issue).
Necessary Headers:
Content-Range: bytes 0-[content length]/[content length]
Content-Transfer-Encoding: binary
Content-Length: [content length]
Accept-Ranges: bytes
I devised this after inspecting a response from MP3s delivered via Akamai's content delivery service.

Related

React Native HTML to PDF not displaying local images

In case you guys don't know, there was a problem previously with this library not rendering local images on Android as well, but apparently it was solved. Now, I'm facing the exact same issue on iOS, with a difference that I can use static images like assets/src/assets/images/logo.png. But when the images start with something like file:///, storage://, ph:// it simply does not get rendered.
What I'm doing is trying to generate a PDF report file, which must be generated independently the user has an internet connection or not. That is the reason why I have to use local images.
The static image is the logo of the company, and the local image which is not getting rendered is an image saved to the phone's storage through Image Picker or Camera Roll. The React Native Image component displays the image perfectly, so I don't think I'm using a wrong path.
What I have tried so far:
Removing the file:/// or storage:// or ph:// from the beginning of the path string;
In some cases, when I save an image to the phone's library with Camera Roll, it will return a path that starts with ph:// but without an extension, such as .jpg or .png. I tried to put the extension manually, and still does not make any difference;
I tried to convert the image to base64 using rn-fetch-blob (with RNFetchBlob.fs.base64.encode(path)), but still got no success.
Devices:
iPhone SE with iOS 14 (also simulator iPhone 11 with iOS 15)
MacBook Air 2017 Core i5 1.8GHz and 8gb RAM (macOS Big Sur 11.5.2)
Environment
node: 12.22.7
npm: 6.14.15
react: 16.9.0
react-native: 0.61.5
react-native-html-to-pdf: ^0.11.0 (updating it to 0.12.0 also got me the same result)
Code:
sharePDF = async () => {
try {
this.changeVisibilityOptions(false);
this.changeVisibilityLoading('Gerando PDF...');
let htmlTemplate = '';
htmlTemplate = await getPDFDespesa(this.state);
const pdfOptions = {
html: htmlTemplate,
fileName: 'RelatorioDespesas',
directory: 'Relatorios'
};
let pdfFile = await RNHTMLtoPDF.convert(pdfOptions);
this.changeVisibilityLoading(false);
const shareOptions = {
title: 'Compartilhar com:',
url: `file://${pdfFile.filePath}`,
type: 'application/pdf',
failOnCancel: false
};
const ShareResponse = await Share.open(shareOptions);
} catch (error) {
this.setState({ visibilityLoadingScreen: false });
console.log('Error =>', error);
}
}
Final thoughts:
Well, since the code is stored at a private repository, I can't show the whole thing here for ethical reasons. But I'm doing my best to give you as much details as possible.
The output the code produces an almost complete PDF, with the only point that I see broken image icons where the images were supposed to be. For Android it works perfectly now.
I think this might be an issue related to WebView, since react-native-html-to-pdf uses WebView to generate the PDF from HTML code. I reached this conclusion after another developer at my job was trying to create a screen with a preview of the PDF before it could be shared got the very same problem for both Android and iOS. The library he used was react-native-webview.
Update with solution
Alright guys, after a long time of research, me and a colleague got to a solution which may not be the best but does what we expected.
First of all, one thing that was discovered is that we have to divide the problem in two, because we actually had two problems.
Images from react-native-image-picker: After a long time trying to find the problem which was preventing the local images from getting rendered, I tried updating the library to version 4.7.3 (latest version at that day) and did a number of required changes to the code, as the version we were using was considerably aged. Well, it happened to work out for my surprise, even with the response uri's format not being changed;
Images from #react-native-community/cameraroll: This one was a bit more complicated. It took me some time to realize that the iOS' PHAsset was not supported in the WebView or react-native-html-to-pdf (which uses WebView in background). So, after some research, me and my colleague found a workaround that lead us to a relatively easy solution. Basically we used react-native-fs to copy the PHAsset media file to a temporary directory, which would return a uri that started with file:// and could be rendered by WebView. That's the code we used to do this:
export default function getImageNameFromUrl(imageUrl = "") {
if (imageUrl) {
const splittedImageUrl = imageUrl.split('/');
return splittedImageUrl.pop();
}
return null;
};
export default async function copyAssetsFileIOSAndReturnURI(remoteURL = '', localURI = '') {
try {
if (remoteURL && localURI) {
const imageName = getImageNameFromUrl(remoteURL);
const imgPath = await RNFS.copyAssetsFileIOS(localURI, RNFS.TemporaryDirectoryPath+imageName, 0, 0);
return imgPath;
}
return null;
} catch (err) {
console.log(err);
return null;
}
}

How to copy from Storage to FileSystemStorage in Codenameone and display in BrowserComponent

I've been reading a lot of StackOverflow posts that discuss copying data from FileSystemStorage to Storage in CodenameOne, such as described in this answer from Shai, as seen below:
InputStream stream =
FileSystemStorage.getInstance().openInputStream(i);
OutputStream out =
Storage.getInstance().createOutputStream("MyImage");
Util.copy(stream, out);
Util.cleanup(stream);
Util.cleanup(out);`
I've been trying to do the reverse: save from Storage to FileSystemStorage in order to show a PDF in the BrowserComponent (while using iOS), but have not been able to do so. I need to show the PDF within the app (so I don't want to use Display.getInstance().execute()).
Basically, I'm trying to dynamically populate a Container with whatever files the user selects-- I am using the FileChooser library for CN1 from Steve Hannah. (Disclaimer: I have made slight modifications to this library as it used in the app I'm working on-- HOWEVER, when I choose images with this library and pull them from Storage to an Image via InputStream, they display perfectly in an ImageViewer so I know that all files are being saved correctly in Storage.)
Here is my code (with help from Steve Hannah's comment on GitHub):
//fileLocation and fileName are slightly different but both end with file extension
File file = new File(fileToUpload.getFileName());
FileSystemStorage fss = FileSystemStorage.getInstance();
InputStream is = Storage.getInstance().createInputStream(fileToUpload.getLocation());
OutputStream os = fss.openOutputStream(file.getAbsolutePath());
Util.copy(is, os);
ToastBar.Status status = ToastBar.getInstance().createStatus();
String message = file.exists() + " " + file.isFile() + file.getAbsolutePath();
status.setMessage(message);
status.setExpires(3000);
status.show();
NativeLogs.getNativeLogs();
if (Display.getInstance().getPlatformName().equals("ios")) {
//Log.p("in ios !!!!");
BrowserComponent browserComponent = new BrowserComponent();
browserComponent.setURL(file.getPath());
horizontalContainer.add(browserComponent);
}
The ToastBar displays true and true for file.exists() and file.isFile().
I stipulate iOS because as far as I've seen while researching previewing PDFs within an app, I've seen that Android needs to have a different implementation, like adding a NativeInterface with an Android library. I also saw in different answers on the Google Group that this functionality (using browserComponent to view PDFs) is only available for iOS and not on the simulator. In the simulator, I see a blank space. My iPhone just freezes and/or crashes after displaying the ToastBar (and I work on a Windows machine, so not much ability to see native logs....)
What can I do to access the file and show it in the BrowserComponent?
Thank you!
Simple solution -- the file had a space in it (eg. "Test page.pdf") and didn't show! When I used files that didn't have spaces this worked and after removing spaces in the file names, thankfully everything worked. I'll have to add code to handle this scenario.
Thanks for your help!

Downloading large pdf files in a Flutter app

I'm looking for a way to download large pdf files from an external server with a Flutter application for offline storage.
But downloading a large file (sometimes 100mb+) takes some time. I don't want the app being stuck in a wait function for it to download. What i'm looking for is a download function that has a callback with a progress report (Something like: 250000/500000 bytes done. Doesn't have to be exactly that. Just something that I can work with and make a progress bar out of).
Is this even possible to do in Flutter? The only things I came across were the HTTP library. But that does not seem to have a progress callback and just plainly reading the contents of a http call (Which also doesn't have a progress report). I hope someone has a method for me that I can use to make this happen.
Kind regards,
Kevin Walter
EDIT:
C# has the perfect example of what I mean
https://stackoverflow.com/a/9459441/2854656
https://docs.flutter.io/flutter/dart-io/HttpClient-class.html
https://docs.flutter.io/flutter/dart-io/HttpClientResponse-class.html
int fileSize;
int downloadProgress = 0;
new HttpClient().get('localhost', 80, '/file.txt')
.then((HttpClientRequest request) => request.close())
.then((HttpClientResponse response) {
fileSize ??= respone.contentLength;
response.transform(utf8.decoder).listen((contents) {
downloadProgres += contents.length;
// handle data
});
});

Blackberry java radio streaming

I'm developing a radio app for BB 5.0 in java. I don't find a way to play the radio from the url stream address that I have. I use multiple formats but nothing works (.pls, .aac, .m3u). I get a RuntimeException every time I try to play the stream. The content is ok, I've checked it.
InputStream stream = Connector.openInputStream(urlPlay);
StreamConnection streamConnection = (StreamConnection) Connector.open(urlPlay, Connector.READ);
InputStream readAhead = streamConnection.openDataInputStream();
byte[] audioData = new byte[500];
readAhead.read(audioData,0,audioData.length);
ByteArrayInputStream in2 = new ByteArrayInputStream(audioData);
player = javax.microedition.media.Manager.createPlayer(in2, "audio/aac");
System.out.println("REALIZE");
player.realize();
System.out.println("PREFETCH");
player.prefetch();
System.out.println("START");
player.start();
Edit:
When I use a URL from my .pls file I hear a little bit of my streaming but It stops immediately.
I suspect the problem is that you are trying to play playlist files instead of an actual stream. Generally, you need to parse those files yourself to get the real stream URLs.
If you open up that .m3u file, you will see that it is just a list of URLs. Take one of those URLs and then try it. Also, be sure you are setting the right content type. You can determine what that type is with cURL or VLC.

PDF file download not working only for iPad specific browsers

A PDF file is generated on server side and pushed to client end for download. While the download works in all browsers on windows , fails on IPAD.
Please advise.
Specification : OS 6, Safari 6.1 , Chrome 23.0.1271.100
Please note: In this application pdf is not downloaded on to a new url (NO REDIRECTION).
General behavior: IPad browser (safari / chrome) does not support the download window, hence its expected to open the pdf and provides option to view in pdf compatible apps. Which is not currently happening.
When i debug the below servlet action code for download, the pdf file is successfully generated on server but browser on Ipad does not show :-(
Code sample :
/** Setting response Header **/
response.setHeader("Content-Type", "application/pdf");
response.setHeader("Content-Disposition", "attachment;filename=sample.pdf");
response.setHeader("Connection", "close");
response.setHeader("Cache-Control","cache");
response.setHeader("Pragma","cache");
response.setDateHeader ("Expires", dt.getTime() + 100000);
/** Writing to output **/
InputStream stream = info.getInputStream();
OutputStream os = response.getOutputStream();
try {
response.setContentType(contentType);
copy(stream, response.getOutputStream());
}
finally {
if (stream != null) {
stream.close();
}
}
After testing, the download action code pasted above appeared to be working fine as the issue was BROWSER specific.
Andriod Tablet - Firefox browser downloads the pdf onto pop up window.
IPad : Safari - FIX: Forced the content to open up in a new tab, something like this :
window.open(print_url);
where print_url is the baseurl+action.do+additional_parameter.
To display a pdf instead of asking the browser to save it, use "inline" instead of "attachment".
response.setHeader("Content-Disposition", "inline;filename=sample.pdf");
Also, you're setting content type twice, once in the header and once using setContentType().
I'm not sure if those two headers interact or cancel each other out, so can't say for sure that it's a source of error, but it seems like something to consider changing.

Resources