RandomAccessFile in Android raw resource file - android

I tried to create a RandomAccessFile object from a raw resource file in android resource directory without success.
I'm only able to get a inputstream object from raw resource file.
getResources().openRawResource(R.raw.file);
Is it possible to create a RandomAccessFile object from raw asset file or Do I need to stick with inputstream?

It's simply not possible to seek forward and back in an input stream without buffering everything in between into memory. That can be extremely costly, and isn't a scalable solution for reading a (binary) file of some arbitrary size.
You're right: ideally, one would use a RandomAccessFile, but reading from the resources provides an input stream instead. The suggestion mentioned in the comments above is to use the input stream to write the file to the SD card, and randomly access the file from there. You could consider writing the file to a temporary directory, reading it, and deleting it after use:
String file = "your_binary_file.bin";
AssetFileDescriptor afd = null;
FileInputStream fis = null;
File tmpFile = null;
RandomAccessFile raf = null;
try {
afd = context.getAssets().openFd(file);
long len = afd.getLength();
fis = afd.createInputStream();
// We'll create a file in the application's cache directory
File dir = context.getCacheDir();
dir.mkdirs();
tmpFile = new File(dir, file);
if (tmpFile.exists()) {
// Delete the temporary file if it already exists
tmpFile.delete();
}
FileOutputStream fos = null;
try {
// Write the asset file to the temporary location
fos = new FileOutputStream(tmpFile);
byte[] buffer = new byte[1024];
int bufferLen;
while ((bufferLen = fis.read(buffer)) != -1) {
fos.write(buffer, 0, bufferLen);
}
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {}
}
}
// Read the newly created file
raf = new RandomAccessFile(tmpFile, "r");
// Read your file here
} catch (IOException e) {
Log.e(TAG, "Failed reading asset", e);
} finally {
if (raf != null) {
try {
raf.close();
} catch (IOException e) {}
}
if (fis != null) {
try {
fis.close();
} catch (IOException e) {}
}
if (afd != null) {
try {
afd.close();
} catch (IOException e) {}
}
// Clean up
if (tmpFile != null) {
tmpFile.delete();
}
}

Why not get a new AssetFileDescriptor each time you need a seek? It seems not to be a cpu cycles intensive task (or is it?)
//seek to your first start position
InputStream ins = getAssets().openFd("your_file_name").createInputStream();
isChunk.skip(start);
//read some bytes
ins.read(toThisBuffer, 0, length);
//later on
//seek to a different position, need to openFd again!
//because createInputStream can be called on asset file descriptor only once.
//This resets the new stream to file offset 0,
//so need to seek (skip()) to a new position relative to file beginning.
ins = getAssets().openFd("your_file_name").createInputStream();
ins.skip(start2);
//read some bytes
ins.read(toThatBuffer, 0, length);
I've used this method in my app that needs random access to a 20Mb resource file hundreds of times per second.

Related

Saving an DataItemAsset received from Android Wear

I'm relatively new to Android. I'm transferring a file from an Android Wear device to a phone, which I did through PutDataRequest. On the phone side I get a DataItemAsset which can provide me a file descriptor using Wearable.DataApi.getFdForAsset(). My question is how do I save this file to external storage?
Thank you!
Here's how I managed to upload a text file from an Android Wear watch to it's paired mobile phone. There may be a simpler way, but this is what worked for me.
(1) On the watch side, create a text file, and read it into an Asset which you can put through the DataApi:
public void SendTextFile()
{
// Get folder for output
File sdcard = Environment.getExternalStorageDirectory();
File dir = new File(sdcard.getAbsolutePath()+ "/MyAppFolder/");
if (!dir.exists()) {dir.mkdirs();} // Create folder if needed
final File file = new File(dir, "test.txt");
if (file.exists()) file.delete();
// Write a text file to external storage on the watch
try {
Date now = new Date();
long nTime = now.getTime();
FileOutputStream fOut = new FileOutputStream(file);
PrintStream ps = new PrintStream(fOut);
ps.println("Time = "+Long.toString(nTime)); // A value that changes each time
ps.close();
} catch (Exception e) {
}
// Read the text file into a byte array
FileInputStream fileInputStream = null;
byte[] bFile = new byte[(int) file.length()];
try {
fileInputStream = new FileInputStream(file);
fileInputStream.read(bFile);
fileInputStream.close();
} catch (Exception e) {
}
// Create an Asset from the byte array, and send it via the DataApi
Asset asset = Asset.createFromBytes(bFile);
PutDataMapRequest dataMap = PutDataMapRequest.create("/txt");
dataMap.getDataMap().putAsset("com.example.company.key.TXT", asset);
PutDataRequest request = dataMap.asPutDataRequest();
PendingResult<DataApi.DataItemResult> pendingResult = Wearable.DataApi
.putDataItem(mGoogleApiClient, request);
}
(2) On the mobile side, receive the asset and write it back out to a file:
public void onDataChanged(DataEventBuffer dataEvents) {
for (DataEvent event : dataEvents) {
if (event.getType() == DataEvent.TYPE_CHANGED &&
event.getDataItem().getUri().getPath().equals("/txt"))
{
// Get the Asset object
DataMapItem dataMapItem = DataMapItem.fromDataItem(event.getDataItem());
Asset asset = dataMapItem.getDataMap().getAsset("com.example.company.key.TXT");
ConnectionResult result =
mGoogleApiClient.blockingConnect(10000, TimeUnit.MILLISECONDS);
if (!result.isSuccess()) {return;}
// Convert asset into a file descriptor and block until it's ready
InputStream assetInputStream = Wearable.DataApi.getFdForAsset(
mGoogleApiClient, asset).await().getInputStream();
mGoogleApiClient.disconnect();
if (assetInputStream == null) { return; }
// Get folder for output
File sdcard = Environment.getExternalStorageDirectory();
File dir = new File(sdcard.getAbsolutePath() + "/MyAppFolder/");
if (!dir.exists()) { dir.mkdirs(); } // Create folder if needed
// Read data from the Asset and write it to a file on external storage
final File file = new File(dir, "test.txt");
try {
FileOutputStream fOut = new FileOutputStream(file);
int nRead;
byte[] data = new byte[16384];
while ((nRead = assetInputStream.read(data, 0, data.length)) != -1) {
fOut.write(data, 0, nRead);
}
fOut.flush();
fOut.close();
}
catch (Exception e)
{
}
// Rescan folder to make it appear
try {
String[] paths = new String[1];
paths[0] = file.getAbsolutePath();
MediaScannerConnection.scanFile(this, paths, null, null);
} catch (Exception e) {
}
}
}
}
You will also need to add the following permission to your manifests at both ends to write to external storage: android.permission.WRITE_EXTERNAL_STORAGE
Note: the most frustrating thing to watch out for is this: if the data does not change, no transfer will occur. So, when you're testing if you write the same data file contents twice, it will only come across the first time - even if you deleted the file from the first run. I lost quite a few hours to this insidious feature of the DataApi ! That's why my code above is writing the current time into the text file.
Also, of course make sure that you set up the GoogleApiClient object to connect, add listeners, etc as described here:
http://developer.android.com/training/wearables/data-layer/index.html

Saving ByteArray of audio into an Audio File

I'm working on a test app to integrate soundtouch (an open source audio processing library) on Android.
My test app already can receive input from the mic, pass the audio thru soundtouch and output the processed audio to an AudioTrack instance.
My question is, how can I change the output from AudioTrack to a new File on my device?
Here's the relevant code in my app (where I'm processing the output of soundtouch, into the input for AudioTrack)
// the following code is a stripped down version of my code
// in no way its supposed to compile or work. Its here for reference purposes
// pre-conditions
// parameters - input : byte[]
soundTouchJNIInstance.putButes(input);
int bytesReceived = soundTouchJNIInstance.getBytes(input);
audioTrackInstance.write(input, 0, bytesReceived);
Any ideas on how to approach this problem? Thanks!
Hope you are already getting the input voice from microphone and saved on a file.
Firstly, import JNI libraries to your oncreate method :
System.loadLibrary("soundtouch");
System.loadLibrary("soundstretch");
Soundstrech library :
public class AndroidJNI extends SoundStretch{
public final static SoundStretch soundStretch = new SoundStretch();
}
Now you need to call soundstrech.process with the input file path and the desired output file to store processed voice as parameters :
AndroidJNI.soundStretch.process(dataPath + "inputFile.wav", dataPath + "outputFile.wav", tempo, pitch, rate);
File sound = new File(dataPath + "outputFile.wav");
File sound2 = new File(dataPath + "inputFile.wav");
Uri soundUri = Uri.fromFile(sound);
The soundUri can be provided as a source to media player for play back :
MediaPlayer mediaPlayer = MediaPlayer.create(this, soundUri);
mediaPlayer.start();
Also note that, the sample size for recording should be selected dynamically by declaring an Array of Sample Rates :
int[] sampleRates = { 44100, 22050, 11025, 8000 }
The best way to write byteArray this :
public void writeToFile(byte[] array)
{
try
{
String path = "Your path.mp3";
File file = new File(path);
if (!file.exists()) {
file.createNewFile();
}
FileOutputStream stream = new FileOutputStream(path);
stream.write(array);
} catch (FileNotFoundException e1)
{
e1.printStackTrace();
}
}
I am not aware of sound touch at all and the link i am providing no where deals with jni code, but you can have a look at it if it helps you any way: http://i-liger.com/article/android-wav-audio-recording
I think the best way to achieve this is converting that audio to a byte[] array. Assuming you have already done that (if not, comment it and I'll provide an example), the above code should work. This assumes you're saving it in a external sdcard in a new directory called AudioRecording and saving it as audiofile.mp3.
final File soundFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "AudioRecording/");
soundFile.mkdirs();
final File outFile = new File(soundFile, 'audiofile.mp3');
try {
final FileOutputStream output = new FileOutputStream(outFile);
output.write(yourByteArrayWithYourAudioFileConverted);
output.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
mkdirs() method will try to construct all the parent directories if they're missing. So if you're planning to store in a 2 or more level depth directory, this will create all the structure.
I use a simple test code snippet to write my audio byte arrays:
public void saveAudio(byte[] array, string pathAndName)
{
FileOutputStream stream = new FileOutputStream(pathAndName);
try {
stream.write(array);
} finally {
stream.close();
}
}
You will probably need to add some exception handling if you are going to be using this in a production environment, but I utilise the above to save audio whenever I am am in the development phase or for personal non-release projects.
Addendum
After some brief thought I have changed my snippet to the following slightly more robust format:
public void saveAudio(byte[] array, string pathAndName)
{
try (FileOutputStream stream= new FileOutputStream(pathAndName)) {
stream.write(array);
} catch (IOException ioe) {
ioe.printStackTrace();
} finally {
stream.close();
}
}
You can use the method using SequenceInputStream, in my app I just merge MP3 files in one and play it using the JNI Library MPG123, but I tested the file using MediaPlayer without problems.
This code isn't the best, but it works...
private void mergeSongs(File mergedFile,File...mp3Files){
FileInputStream fisToFinal = null;
FileOutputStream fos = null;
try {
fos = new FileOutputStream(mergedFile);
fisToFinal = new FileInputStream(mergedFile);
for(File mp3File:mp3Files){
if(!mp3File.exists())
continue;
FileInputStream fisSong = new FileInputStream(mp3File);
SequenceInputStream sis = new SequenceInputStream(fisToFinal, fisSong);
byte[] buf = new byte[1024];
try {
for (int readNum; (readNum = fisSong.read(buf)) != -1;)
fos.write(buf, 0, readNum);
} finally {
if(fisSong!=null){
fisSong.close();
}
if(sis!=null){
sis.close();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
if(fos!=null){
fos.flush();
fos.close();
}
if(fisToFinal!=null){
fisToFinal.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

mp3 not saving to sd correctly; how to save mp3 to sd card?

I've been looking at this site for the past 3 or so hours. How to copy files from 'assets' folder to sdcard?
This is the best I could come up with because I'm only trying to copy one file at a time.
InputStream in = null;
OutputStream out = null;
public void copyAssets() {
try {
in = getAssets().open("aabbccdd.mp3");
File outFile = new File(root.getAbsolutePath() + "/testf0lder");
out = new FileOutputStream(outFile);
copyFile(in, out);
in.close();
in = null;
out.flush();
out.close();
out = null;
} catch (IOException e) {
Log.e("tag", "Failed to copy asset file: ", e);
}
}
private void copyFile(InputStream in, OutputStream out) throws IOException {
byte[] buffer = new byte[1024];
int read;
while ((read = in.read(buffer)) != -1) {
out.write(buffer, 0, read);
}
}
I've figured out how to create a file and save a text file. http://eagle.phys.utk.edu/guidry/android/writeSD.html
I would rather save an mp3 file to the sdcard rather than a text file.
When I use this code I provided, I get a text document that same size as the aabbccdd.mp3 file. It does not create a folder and save an .mp3 file. It saves a text document in the root folder. When you open it, I see a whole bunch of chinese letters, but at the top in English I can see the words WireTap. WireTap Pro was the program I used to record the sound so I know the .mp3 is passing through. It's just not creating a folder and then saving a file like the above .edu example.
What should I do?
I think you should do something like that -[Note: this i used for some other formats not mp3 but its works on my app for multiple format so i hope it will work for u too.]
InputStream in = this.getAssets().open("tmp.mp3"); //give path as per ur app
byte[] data = getByteData(in);
Make sure u have the folder already exists on path, if folder is not there it will not save content correctly.
byteArrayToFile(data , "testfolder/tmp.mp3"); //as per ur sdcard path, modify it.
Now the methods ::
1) getByteData from inputstream -
private byte[] getByteData(InputStream is)
{
byte[] buffer= new byte[1024]; /* or some other number */
int numRead;
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
try{
while((numRead = is.read(buffer)) > 0) {
bytes.write(buffer, 0, numRead);
}
return bytes.toByteArray();
}
catch(Exception e)
{ e.printStackTrace(); }
return new byte[0];
}
2) byteArrayToFile
public void byteArrayToFile(byte[] byteArray, String outFilePath){
FileOutputStream fos;
try {
fos = new FileOutputStream(outFilePath);
fos.write(byteArray);
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}

Setting Google Glass Card image with file URI fails

I'm having issues using the Cards from the recently released GDK. Basically, Card.addImage() can only take two arguments, a resource id or a URI.
For my use case, I need to open an image that exists as a file not directly as a resource. So for testing purposes I'm including the images in the assets folder. Trying to access them directly from the asset folder fails, so I'm copying them from there to internal storage. Once they're copied, I generate a URI from the file and assign it to the card. The resulting card shows a grey block where the image should be.
String fileName = step.attachment; //of the form, "folder1/images/image1.jpg"
File outFile = new File(getFilesDir()+File.separator+fileName);
FileChannel inputChannel = null;
FileChannel outputChannel = null;
try {
//check to see if the file has already been cached in internal storage before copying
if(!outFile.exists()) {
FileInputStream inputStream = new FileInputStream(getAssets().openFd(fileName).getFileDescriptor());
FileOutputStream outputStream = openFileOutput(fileName, Context.MODE_PRIVATE);
inputChannel = inputStream.getChannel();
outputChannel = outputStream.getChannel();
outputChannel.transferFrom(inputChannel, 0, inputChannel.size());
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
finally{
try {
if(inputChannel!=null)
inputChannel.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
if(outputChannel!=null)
outputChannel.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
card.addImage(Uri.fromFile(outFile));
It's hard to diagnose because I have no clue what the Card is doing internally.
Instead of writing
new FileInputStream(getAssets().openFd(fileName).getFileDescriptor());
can you try
getAssets().openFd(fileName).createInputStream();
and see if it works?
To answer your original question, the addImage method supports resource: and file: URIs.
This is very strange, but I managed to solve my problem. I replaced the file copy code with the following and it appears to have solved my issues
InputStream in = null;
OutputStream out = null;
try {
in = getAssets().open(step.attachment);
out = new FileOutputStream(outFile);
byte[] buffer = new byte[1024];
int read;
while((read = in.read(buffer)) != -1){
out.write(buffer, 0, read);
}
in.close();
in = null;
out.flush();
out.close();
out = null;
} catch(IOException e) {
Log.e("tag", "Failed to copy asset file: " + step.attachment, e);
}
It's not clear to me why/how I was copying my entire apk, but I'm guessing it's the call to
getAssets().openFd(fileName).getFileDescriptor()
Perhaps it was returning the file descriptor of the apk. It's odd because I've seen some claim that the previous method works.

How to copy large files in Android?

I am trying to copy a large pdf-file (3.7 mb) from my raw-folder to the external cache directory.
I a using the following piece of code:
int i = 0;
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()))
{
InputStream input = getResources().openRawResource(pdfs[i]);
File file = new File(Environment.getExternalStorageDirectory(), "/Android/data/eu.app/cache/" + pdfNames[i]);
if(!file.exists())
{
try
{
new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/Android/data/eu.app/cache").mkdirs();
FileOutputStream fos = new FileOutputStream(file.toURI().getPath(), false);
OutputStream os = new BufferedOutputStream(fos);
byte[] buffer = new byte[1024];
int byteRead = 0;
while ((byteRead = input.read(buffer)) != -1) {
os.write(buffer, 0, byteRead);
}
fos.close();
}
catch(Exception ex)
{
Log.d("storage", ex.getMessage());
}
}
}
else
{
}
I don't get any errors, but the output-file is a few bytes smaller than the original and cannot be opened.
What do I need to do to fix this?
I think the main issue is that you close fos while you should close os. You also need to put the close operation in a finally block.
Update (now with a full keyboard ;)): You close the file stream (fos) before the buffered stream is flushed. What you should do is to close the buffered stream (os), and that will in turn flush its buffer and write those bytes that are missing, and then it will automatically close the underlying file stream. To fix it change fos.close() into os.close().
In addition, to make sure that you always close the stream you should place the close operation in a finally block. A typical pattern is the following:
BufferedInputStream in = null;
try {
in = new BufferedInputStream(anInputStream);
BufferedOutputStream out = null;
try {
out = new BufferedOutputStream(new FileOutputStream(aFile));
// Read and write what you should write
}
finally {
if (out != null) out.close();
}
} finally {
if (in != null) in.close();
}
You can easily add an input stream, but be careful to make sure all streams are closed. This can be handled by nesting finally blocks or nesting try-catch blocks inside the finally block.
Either you throw an IOException from this method and handle it outside (often preferred), or you wrap the above code in a new try-catch statement and handle it there. However, handling it within the method mixes UI with logic and the code is often clearer separating UI and logic.
A final note: 1024 is rather small. Play with different values. On the other hand the buffered stream will handle the buffering for you.
I've been using this function for reading from one stream to another for a few years and have never had any problems with the resulting file. Just open the source and target files as you are and pass their respective streams into this function:
public static void streamToStream(InputStream is, OutputStream os) {
int count = 0;
try {
while(count != -1) {
byte[] bytes = new byte[2048];
count = is.read(bytes);
if(count == -1) {
continue;
}
os.write(bytes, 0, count);
bytes = null;
}
} catch (Exception e) {
e.printStackTrace();
}
}

Categories

Resources