Incorrect file being produced using websockets in helidon - websocket

I am trying to upload a file using websockets in Helidon.I think i am doing it write the right way but the code seems to be flaky in terms of the size of the file produced which is different. The size of the file being produced is different for different runs.
How can i make sure that the file size is same on both ends?
I use a simple protocol for handshake[code below]:
Step1 client sends filesize=11000 buffer=5000
Step2 server sends SENDFILE
Step3 client >> buffer 1 server >> write 1 5000
Step4 client >> buffer 2 server >> write 2 5000
Step5 client >> buffer 3 server >> write 3 1000
Step6 client sends ENDOFFILE server >> session.close
//SERVER side OnOpen session below
session.addMessageHandler(new MessageHandler.Whole<String>() {
#Override
public void onMessage(String message) {
System.out.println("Server >> " + message);
if (message.contains("FILESIZE")) {
session.getBasicRemote().sendText("SENDFILENOW");
}
if(message.contains("ENDOFFILE")) {
System.out.println("Server >> FILE_SIZE=" + FILE_SIZE);
finalFileOutputStream.close();
session.close();
}
}
});
session.addMessageHandler(new MessageHandler.Whole<ByteBuffer>() {
#Override
public void onMessage(ByteBuffer b) {
finalFileOutputStream.write(b.array(), 0, b.array().length);
finalFileOutputStream.flush();
}
});
//CLIENT OnOpen session below
session.getBasicRemote().sendText("FILESIZE=" + FILE_SIZE);
session.addMessageHandler(new MessageHandler.Whole<String>() {
#Override
public void onMessage(String message) {
long M = FILE_SIZE / BUFFER_SIZE;
long R = FILE_SIZE % BUFFER_SIZE;
if(!message.equals("SENDFILENOW"))
return;
try {
System.out.println("Starting File read ... " + path + " " + FILE_SIZE + " " + M + " " +message );
byte[] buffer = new byte[(int) BUFFER_SIZE];
while (M > 0) {
fileInputStream.read(buffer);
ByteBuffer bytebuffer = ByteBuffer.wrap(buffer);
session.getBasicRemote().sendBinary(bytebuffer);
M--;
}
buffer = new byte[(int) R];
fileInputStream.read(buffer, 0, (int) R);
fileInputStream.close();
ByteBuffer bytebuffer = ByteBuffer.wrap(buffer);
session.getBasicRemote().sendBinary(bytebuffer);
session.getBasicRemote().sendText("FILEREADDONE");
session.close();
f.complete(true);
} catch (IOException e) {
fail("Unexpected exception " + e);
}
}
});

Your solution is unnecessarily built on top of several levels of abstraction just to use websockets. Do you really need that? Helidon is very well equipped to handle huge file upload directly and much more efficiently.
public class LargeUpload {
public static void main(String[] args) {
ExecutorService executor = ThreadPoolSupplier.create("upload-thread-pool").get();
WebServer server = WebServer.builder(Routing.builder()
.post("/streamUpload", (req, res) -> req.content()
.map(DataChunk::data)
.flatMapIterable(Arrays::asList)
.to(IoMulti.writeToFile(createFile(req.queryParams().first("fileName").orElse("bigFile.mkv")))
.executor(executor)
.build())
.onError(res::send)
.onComplete(() -> {
res.status(Http.Status.ACCEPTED_202);
res.send();
}).ignoreElement())
.build())
.port(8080)
.build()
.start()
.await(Duration.ofSeconds(10));
// Server started - do upload
//several gigs file
Path file = Path.of("/home/kec/helidon-kafka.mkv");
try (FileInputStream fis = new FileInputStream(file.toFile())) {
WebClient.builder()
.baseUri("http://localhost:8080")
.build()
.post()
.path("/streamUpload")
.queryParam("fileName", "bigFile_" + System.currentTimeMillis() + ".mkv")
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.submit(IoMulti.multiFromByteChannelBuilder(fis.getChannel())
.bufferCapacity(1024 * 1024 * 4)
.build()
.map(DataChunk::create)
)
.await(Duration.ofMinutes(10));
} catch (IOException e) {
throw new RuntimeException(e);
}
executor.shutdown();
server.shutdown()
.await(Duration.ofSeconds(10));
}
static Path createFile(String path) {
try {
Path filePath = Path.of("/home/kec/tmp/" + path);
System.out.println("Creating " + filePath);
return Files.createFile(filePath);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

Related

is it possible to read the content of the file present in the ftp server? [duplicate]

This is re-worded from a previous question (which was probably a bit unclear).
I want to download a text file via FTP from a remote server, read the contents of the text file into a string and then discard the file. I don't need to actually save the file.
I am using the Apache Commons library so I have:
import org.apache.commons.net.ftp.FTPClient;
Can anyone help please, without simply redirecting me to a page with lots of possible answers on?
Not going to do the work for you, but once you have your connection established, you can call retrieveFile and pass it an OutputStream. You can google around and find the rest...
FTPClient ftp = new FTPClient();
...
ByteArrayOutputStream myVar = new ByteArrayOutputStream();
ftp.retrieveFile("remoteFileName.txt", myVar);
ByteArrayOutputStream
retrieveFile
Normally I'd leave a comment asking 'What have you tried?'. But now I'm feeling more generous :-)
Here you go:
private void ftpDownload() {
FTPClient ftp = null;
try {
ftp = new FTPClient();
ftp.connect(mServer);
try {
int reply = ftp.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
throw new Exception("Connect failed: " + ftp.getReplyString());
}
if (!ftp.login(mUser, mPassword)) {
throw new Exception("Login failed: " + ftp.getReplyString());
}
try {
ftp.enterLocalPassiveMode();
if (!ftp.setFileType(FTP.BINARY_FILE_TYPE)) {
Log.e(TAG, "Setting binary file type failed.");
}
transferFile(ftp);
} catch(Exception e) {
handleThrowable(e);
} finally {
if (!ftp.logout()) {
Log.e(TAG, "Logout failed.");
}
}
} catch(Exception e) {
handleThrowable(e);
} finally {
ftp.disconnect();
}
} catch(Exception e) {
handleThrowable(e);
}
}
private void transferFile(FTPClient ftp) throws Exception {
long fileSize = getFileSize(ftp, mFilePath);
InputStream is = retrieveFileStream(ftp, mFilePath);
downloadFile(is, buffer, fileSize);
is.close();
if (!ftp.completePendingCommand()) {
throw new Exception("Pending command failed: " + ftp.getReplyString());
}
}
private InputStream retrieveFileStream(FTPClient ftp, String filePath)
throws Exception {
InputStream is = ftp.retrieveFileStream(filePath);
int reply = ftp.getReplyCode();
if (is == null
|| (!FTPReply.isPositivePreliminary(reply)
&& !FTPReply.isPositiveCompletion(reply))) {
throw new Exception(ftp.getReplyString());
}
return is;
}
private byte[] downloadFile(InputStream is, long fileSize)
throws Exception {
byte[] buffer = new byte[fileSize];
if (is.read(buffer, 0, buffer.length)) == -1) {
return null;
}
return buffer; // <-- Here is your file's contents !!!
}
private long getFileSize(FTPClient ftp, String filePath) throws Exception {
long fileSize = 0;
FTPFile[] files = ftp.listFiles(filePath);
if (files.length == 1 && files[0].isFile()) {
fileSize = files[0].getSize();
}
Log.i(TAG, "File size = " + fileSize);
return fileSize;
}
You can just skip the download to local filesystem part and do:
FTPClient ftpClient = new FTPClient();
try {
ftpClient.connect(server, port);
ftpClient.login(user, pass);
ftpClient.enterLocalPassiveMode();
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
InputStream inputStream = ftpClient.retrieveFileStream("/folder/file.dat");
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "Cp1252"));
while(reader.ready()) {
System.out.println(reader.readLine()); // Or whatever
}
inputStream.close();
} catch (IOException ex) {
ex.printStackTrace();
} finally {
try {
if (ftpClient.isConnected()) {
ftpClient.logout();
ftpClient.disconnect();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}

SSE server sending events in a batch on final close

I have a Jersey server running locally, it exposes a SSE resource just like the examples here: https://jersey.github.io/documentation/latest/sse.html. I have a local webpack Angular app, that binds to the exposed GET endpoint and listens for data.
On the GET, I start up a thread to send notifications at regular intervals over 6-8 seconds. I don't see anything on the client UNTIL the EventOutput object is closed.
What am I doing wrong, and how can I fix this?
The server code WORKS with just a simple curl, i.e.:
curl http://localhost:8002/api/v1/notify
But on both Chrome and Safari the following code exhibits the behavior
Client (TypeScript):
this.evSource = new EventSource('http://localhost:8002/api/v1/notify');
this.evSource.addEventListener(
'event',
(x => console.log('we have ', x))
);
this.evSource.onmessage = (data => console.log(data));
this.evSource.onopen = (data => console.log(data));
this.evSource.onerror = (data => {
console.log(data);
this.evSource.close();
});
Server (Java):
// cache callback
public void eventCallback(Iterable<CacheEntryEvent<? extends Integer, ? extends Integer>> events) {
for (CacheEntryEvent<? extends Integer, ? extends Integer> x : events) {
LOGGER.info("{} Sending the following value: " + x.getValue(), Thread.currentThread().getId());
final OutboundEvent sseEvent = new OutboundEvent.Builder().name("event")
.data(Integer.class, x.getValue()).build();
this.broadcaster.broadcast(sseEvent);
}
}
#GET
#Produces(SseFeature.SERVER_SENT_EVENTS)
#ApiOperation(value = "Setup SSE pipeline", notes = "Sets up the notification pipeline for clients to access")
#ApiResponses(value = {
#ApiResponse(code = HttpURLConnection.HTTP_UNAUTHORIZED,
message = "Missing, bad or untrusted cookie"),
#ApiResponse(code = HttpURLConnection.HTTP_OK,
message = "Events streamed successfully")
})
#Timed
#ResponseMetered
public EventOutput registerNotificationEvents(
#HeaderParam(SseFeature.LAST_EVENT_ID_HEADER) String lastEventId,
#QueryParam(SseFeature.LAST_EVENT_ID_HEADER) String lastEventIdQuery) {
if (!Strings.isNullOrEmpty(lastEventId) || !Strings.isNullOrEmpty(lastEventIdQuery)) {
LOGGER.info("Found Last-Event-ID header: {}", !Strings.isNullOrEmpty(lastEventId) ? lastEventId : lastEventIdQuery );
}
LOGGER.info("{} Received request", Thread.currentThread().getId());
this.continuation = true;
final EventOutput output = new EventOutput();
broadcaster.add(output);
Random rand = new Random();
IntStream rndStream = IntStream.generate(() -> rand.nextInt(90));
List<Integer> lottery = rndStream.limit(15).boxed().collect(Collectors.toList());
IgniteCache<Integer, Integer> cache = this.ignite.cache(topic_name);
executorService.execute(() -> {
try {
lottery.forEach(value -> {
try {
TimeUnit.MILLISECONDS.sleep(500);
LOGGER.info("{} Sending the following value to Ignite: " + value + " : " + count++, Thread.currentThread().getId());
if (!cache.isClosed()) {
cache.put(1, value);
}
} catch (InterruptedException ex) {
ex.printStackTrace();
}
});
TimeUnit.MILLISECONDS.sleep(500);
continuation = false;
TimeUnit.MILLISECONDS.sleep(500);
if (!output.isClosed()) {
// THIS is where the client sees ALL the data broadcast
// in one shot
output.close();
}
} catch (InterruptedException ex) {
ex.printStackTrace();
} catch (IOException ex) {
ex.printStackTrace();
}
});
LOGGER.info("{} Completing request", Thread.currentThread().getId());
return output;
}
}
Looks like http://github.com/dropwizard/dropwizard/issues/1673 captures the problem. GZip default won't flush even if upper levels ask for it. Solution is something like
((AbstractServerFactory)configuration.getServerFactory()).getGzipFilterFactory().setSyncFlush(true);
will enable flushing to synchronize with GZip if disabling GZip all up is not an option

How to download large zip file (1 -2 GB) using SpringBoot Rest API Apache CXF implementation?

How to download large zip file (1 -2 GB) using SpringBoot Rest API Apache CXF implementation ?
i have tried using Output Stream but no luck.
InputStream inputStream = new FileInputStream(new File(file));
return new StreamingResponseBody() {
#Override
public void writeTo(OutputStream outputStream)
throws IOException {
int nRead;
byte[] data = new byte[1024];
while ((nRead = inputStream.read(data, 0, data.length)) != -1) {
System.out.println("Writing some bytes..");
outputStream.write(data, 0, nRead);
}
inputStream.close();
}
};
I tried below code and it is able to download 500MB file, but it should work for 1-2 GB file as well :
try {
String link = "https://s.basketbuild.com/uploads/devs/dianlujitao/oneplus3/cm13/cm-13.0-20160621-UNOFFICIAL-oneplus3.zip";
URL url = new URL(link);
System.out.println("Started reading the zip");
File dir = new File("C:\\Softwares\\testdownload");
if (!dir.exists())
dir.mkdirs();
String fileBaseName = "TestDownload";
String fileExtension = "zip";
System.out.println("Name: " + fileBaseName + '.' + fileExtension);
File outputFile = new File(dir, fileBaseName + '.' + fileExtension);
if (!outputFile.exists()) {
outputFile.createNewFile();
}
InputStream inputStream = new BufferedInputStream(url.openStream());
OutputStream byteArrayOutputStream = new BufferedOutputStream(new FileOutputStream(outputFile));
int n = 0;
byte[] buf = new byte[1024];
long duration = System.currentTimeMillis();
while ((n = inputStream.read(buf)) != -1) {
byteArrayOutputStream.write(buf, 0, n);
}
duration = System.currentTimeMillis() - duration;
System.out.println("Finish in " + duration + "ms");
inputStream.close();
// release outputstream
byteArrayOutputStream.close();
System.out.println("Your download has been finished");
} catch (MalformedURLException e) {
e.printStackTrace();
System.out.println("Something unexpected has happened!");
} catch (IOException e) {
e.printStackTrace();
System.out.println("Something unexpected has happened!");
}

Receive assets on Handheld

I'm sending files from wear to handheld side. I'm sending 4 or 5 files (2 of them are bigger) but everytime the handheld side only receives ONE of the bigger ones...
wear:
public void sendFile(String filename){
//BLE files
fileToArray("BLEdata/" + filename + "_RightDevice.txt", "/BLEdata/");
//send log file
fileToArray(filename + "_LOG.txt", "/LOGdata/");
//send GPS file
fileToArray("GPSdata/" + filename + "_GPS.txt", "/GPSdata/");
//send Report files
fileToArray("Report/" + filename + "_Report.txt", "/REPORTdata/");
//BLE files
fileToArray("BLEdata/" + filename + "_LeftDevice.txt", "/BLEdata/");
}
public void fileToArray(String filename, String path)
{
FileInputStream fileInputStream = null;
File file = new File(Environment.getExternalStorageDirectory() + "/TuneWear/" + filename);
System.out.println("PATH: " + file.getPath());
if(file.exists()){
byte[] bFile = new byte[(int) file.length()];
try {
//convert file into array of bytes
fileInputStream = new FileInputStream(file);
fileInputStream.read(bFile);
fileInputStream.close();
WearResultsActivity main = (WearResultsActivity) getActivity();
long time = main.getInitTime();
if(filename.contains("_RightDevice")){
new SendToDataLayerThread(path + time, bFile, 2).start();
}else if (filename.contains("_LeftDevice")){
new SendToDataLayerThread(path + time, bFile, 1).start();
} else
new SendToDataLayerThread(path + time, bFile).start();
}catch(Exception e){
e.printStackTrace();
}
} else System.out.println("Doesn't exist:\n" + file.getPath());
}
class SendToDataLayerThread extends Thread {
String path;
byte[] bFile;
int footSide;
// Constructor for sending data objects to the data layer
SendToDataLayerThread(String p, byte[] bytes, int footside) {
path = p;
bFile = bytes;
footSide = footside;
}
SendToDataLayerThread(String p, byte[] bytes) {
path = p;
bFile = bytes;
}
public void run() {
WearResultsActivity main = (WearResultsActivity) getActivity();
GoogleApiClient googleClient = main.getGoogleClient();
Asset asset = Asset.createFromBytes(bFile);
System.out.println(asset.toString());
PutDataMapRequest dataMap = PutDataMapRequest.create(path);
dataMap.getDataMap().putLong("timestamp", Calendar.getInstance().getTimeInMillis());
dataMap.getDataMap().putLong("/InitialTime", ((WearResultsActivity) getActivity()).getInitTime());
dataMap.getDataMap().putAsset("asset", asset);
if(footSide == 1) {
dataMap.getDataMap().putInt("footside", footSide);
System.out.println("DATAMAP COM LEFT " + footSide);
}else if (footSide == 2) {
dataMap.getDataMap().putInt("footside", footSide);
System.out.println("DATAMAP COM RIGHT " + footSide);
}
PutDataRequest request = dataMap.asPutDataRequest();
PendingResult<DataApi.DataItemResult> pendingResult = Wearable.DataApi.putDataItem(googleClient, request);
pendingResult.setResultCallback(new ResultCallback<DataApi.DataItemResult>() {
#Override
public void onResult(DataApi.DataItemResult dataItemResult) {
System.out.println("RESULT");
}
});
//Wearable.DataApi.putDataItem(googleClient, request);
}
}
Handheld:
#Override
public void onDataChanged(DataEventBuffer dataEvents) {
System.out.println("COUNT " + dataEvents.getCount());
for (DataEvent event : dataEvents) {
DataItem item = event.getDataItem();
if (event.getType() == DataEvent.TYPE_CHANGED) {
// DataItem changed
if (item.getUri().getPath().contains(LOGdata)) {
final DataMapItem dataMapItem = DataMapItem.fromDataItem(item);
Asset asset = dataMapItem.getDataMap().getAsset("asset");
//TODO here
Wearable.DataApi.getFdForAsset(googleClient, asset).setResultCallback(
new ResultCallback<DataApi.GetFdForAssetResult>() {
#Override
public void onResult(DataApi.GetFdForAssetResult getFdForAssetResult) {
InputStream assetInputStream = getFdForAssetResult.getInputStream();
long initialTime = dataMapItem.getDataMap().getLong(KEY_INITIALTIME);
String fileName = new SimpleDateFormat("HH'h'mm'm'ss's'_dd-MM-yyyy").format(initialTime);
String dataPath = Environment.getExternalStorageDirectory().toString() + "/TuneWear/";
File myDir = new File(dataPath);
myDir.mkdirs();
File file = new File(myDir, "Run_" + fileName + "_LOG.txt");
System.out.println("FILE: " + file.getPath());
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 (IOException e) {
System.out.println("ERROR File write failed: " + e.toString());
}
}
}
);
} else if (item.getUri().getPath().contains(GPSdata)) {
final DataMapItem dataMapItem = DataMapItem.fromDataItem(item);
Asset asset = dataMapItem.getDataMap().getAsset("asset");
//TODO here
Wearable.DataApi.getFdForAsset(googleClient, asset).setResultCallback(
new ResultCallback<DataApi.GetFdForAssetResult>() {
#Override
public void onResult(DataApi.GetFdForAssetResult getFdForAssetResult) {
InputStream assetInputStream = getFdForAssetResult.getInputStream();
long initialTime = dataMapItem.getDataMap().getLong(KEY_INITIALTIME);
String fileName = new SimpleDateFormat("HH'h'mm'm'ss's'_dd-MM-yyyy").format(initialTime);
String dataPath = Environment.getExternalStorageDirectory().toString() + "/TuneWear/GPSdata/";
File myDir = new File(dataPath);
myDir.mkdirs();
File file = new File(myDir, "Run_" + fileName + "_GPS.txt");
System.out.println("FILE: " + file.getPath());
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 (IOException e) {
System.out.println("ERROR File write failed: " + e.toString());
}
}
}
);
} else if (item.getUri().getPath().contains(REPORTdata)) {
final DataMapItem dataMapItem = DataMapItem.fromDataItem(item);
Asset asset = dataMapItem.getDataMap().getAsset("asset");
//TODO here
Wearable.DataApi.getFdForAsset(googleClient, asset).setResultCallback(
new ResultCallback<DataApi.GetFdForAssetResult>() {
#Override
public void onResult(DataApi.GetFdForAssetResult getFdForAssetResult) {
InputStream assetInputStream = getFdForAssetResult.getInputStream();
long initialTime = dataMapItem.getDataMap().getLong(KEY_INITIALTIME);
String fileName = new SimpleDateFormat("HH'h'mm'm'ss's'_dd-MM-yyyy").format(initialTime);
String dataPath = Environment.getExternalStorageDirectory().toString() + "/TuneWear/Report/";
File myDir = new File(dataPath);
myDir.mkdirs();
File file = new File(myDir, "Run_" + fileName + "_Report.txt");
System.out.println("FILE: " + file.getPath());
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 (IOException e) {
System.out.println("ERROR File write failed: " + e.toString());
}
}
}
);
} else if (item.getUri().getPath().contains(BLEdata)) {
final DataMapItem dataMapItem = DataMapItem.fromDataItem(item);
Asset asset = dataMapItem.getDataMap().getAsset("asset");
//TODO here
Wearable.DataApi.getFdForAsset(googleClient, asset).setResultCallback(
new ResultCallback<DataApi.GetFdForAssetResult>() {
#Override
public void onResult(DataApi.GetFdForAssetResult getFdForAssetResult) {
InputStream assetInputStream = getFdForAssetResult.getInputStream();
long initialTime = dataMapItem.getDataMap().getLong(KEY_INITIALTIME);
String fileName = new SimpleDateFormat("HH'h'mm'm'ss's'_dd-MM-yyyy").format(initialTime);
String dataPath = Environment.getExternalStorageDirectory().toString() + "/TuneWear/BLEdata/";
File myDir = new File(dataPath);
myDir.mkdirs();
File file = null;
System.out.println("FOOT SIIIIIIDE: " + dataMapItem.getDataMap().getInt("footside"));
if(dataMapItem.getDataMap().getInt("footside") == 1){
file = new File(myDir, "Run_" + fileName + "_LeftDevice.txt");
System.out.println("FILE: " + file.getPath());
} else if(dataMapItem.getDataMap().getInt("footside") == 2){
file = new File(myDir, "Run_" + fileName + "_RightDevice.txt");
System.out.println("FILE: " + file.getPath());
}
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 (IOException e) {
System.out.println("ERROR File write failed: " + e.toString());
}
}
}
);
}
} else if (event.getType() == DataEvent.TYPE_DELETED) {
// DataItem deleted
System.out.println("DataItem deleted: " + event.getDataItem().getUri());
}
//Wearable.DataApi.deleteDataItems(googleClient, event.getDataItem().getUri(), DataApi.FILTER_PREFIX);
}
}
I only receive the GPS, Report and LOG data every time. The other 2 files I only receive ONE... Im sending them exactly the same way, but I'm receiving only one of them.
Does anyone detects the error on my code???
EDIT
I just discovered that if the smartwatch is connected to the handheld at the time that the files are sent, they are all received. If they are not connected, one of the files (RightDevice.txt or LeftDevice.txt) are not received when they connect...
I solved it!
It was the most basic error of all.... I was sending the same path for the two files (RightDevice and LeftDevice) so only one of the objects was updated on the googleapiclient.

Android Wear ChannelApi examples?

The latest Android Wear update comes with support for ChannelApi that can be used for sending files to/from wearable or handheld. The problem is I cannot find a single sample of how to use this functionality. The Android samples doesn't include this feature. So if anyone knows how to use the sendFile/receiveFile and can give a quick example here it would be appreciated.
Take a look on this answer to know how to use the Channel API to create the channel between the devices.
After you create the googleClient and retrive the nodeId of the device you want to send the file to, basically you can use the following code on the wearable side:
//opening channel
ChannelApi.OpenChannelResult result = Wearable.ChannelApi.openChannel(googleClient, nodeId, "/mypath").await();
channel = result.getChannel();
//sending file
channel.sendFile(googleClient, Uri.fromFile(file));
Then, on the handheld device:
//receiving the file
#Override
public void onChannelOpened(Channel channel) {
if (channel.getPath().equals("/mypath")) {
file = new File("/sdcard/file.txt");
try {
file.createNewFile();
} catch (IOException e) {
//handle error
}
channel.receiveFile(mGoogleApiClient, Uri.fromFile(file), false);
}
}
//when file is ready
#Override
public void onInputClosed(Channel channel, int i, int i1) {
MainActivity.this.runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(MainActivity.this, "File received!", Toast.LENGTH_SHORT).show();
}
});
}
If you need more information about this, please visit the reference site from Google
This is just an add to the answer: also check your WearableListenerService in androidmanifest. It's intent filter should contain the com.google.android.gms.wearable.CHANNEL_EVENT action.
I have used some code like this with success. Transfers can be fairly slow.
Both the handheld and wearable applications MUST HAVE the same applicationId in their gradle files.
The wearable needs a manifest entry something like this
<service
android:name="com.me.myWearableListenerService"
android:enabled="true"
android:exported="true">
<intent-filter>
<!-- listeners receive events that match the action and data filters -->
<action android:name="com.google.android.gms.wearable.CHANNEL_EVENT"/>
<data android:scheme="wear" android:host="*" android:pathPrefix="/MyAppPath" />
</intent-filter>
</service>
to launch its WearableListenerService when the Handheld sends a file.
private static final String WEARABLE_FILE_COPY = "MyAppPath/FileCopy";
private void copyFileToWearable (final File file, final String nodeId, Context ctx) {
new Thread(new Runnable() {
#Override
public void run() {
final ChannelClient cc = Wearable.getChannelClient(ctx);
ChannelClient.ChannelCallback ccb = new ChannelClient.ChannelCallback() {
#Override
public void onChannelClosed(#NonNull ChannelClient.Channel channel, int i, int i1) {
super.onChannelClosed(channel, i, i1);
Log.d(TAG, "copyFileToWearable " + channel.getNodeId() + " onChannelClosed ");
cc.unregisterChannelCallback(this);
}
#Override
public void onOutputClosed(#NonNull ChannelClient.Channel channel, int i, int i1) {
super.onOutputClosed(channel, i, i1);
Log.d(TAG, "copyFileToWearable " + channel.getNodeId() + " onOutputClosed ");
cc.unregisterChannelCallback(this);
// this is transfer success callback ...
}
};
ChannelClient.Channel c;
Log.d(TAG, "copyFileToWearable transfer file " + file.getName() +
" size:" + file.length()/1000000 + "Mb");
try {
// send the filename to the wearable with the channel open
c = Tasks.await(cc.openChannel(nodeId, WEARABLE_FILE_COPY + "/" + file.getName()));
Log.d(TAG, "copyFileToWearable channel opened to " + nodeId);
Log.d(TAG, "copyFileToWearable register callback");
Tasks.await(cc.registerChannelCallback(c, ccb));
Log.d(TAG, "copyFileToWearable sending file " + file.getName());
Tasks.await(cc.sendFile(c, Uri.fromFile(file)));
// completion is indicated by onOutputClosed
} catch (Exception e) {
Log.w(TAG, "copyFileToWearable exception " + e.getMessage());
cc.unregisterChannelCallback(ccb);
// failure
}
}
}).start();
}
call this from onChannelOpened in a WearableListenerService when c.getPath() starts with WEARABLE_FILE_COPY
private void receiveFileFromHandheld(final ChannelClient.Channel c, File myStorageLocation, Context ctx) {
// filename sent by the handheld is at the end of the path
String[] bits = c.getPath().split("\\/");
// store in a suitable spot
final String receivedFileName = myStorageLocation.getAbsolutePath() + "/" + bits[bits.length-1];
new Thread(new Runnable() {
#Override
public void run() {
final ChannelClient cc = Wearable.getChannelClient(ctx);
ChannelClient.ChannelCallback ccb = new ChannelClient.ChannelCallback() {
boolean mClosed = false;
#Override
public void onChannelClosed(#NonNull ChannelClient.Channel channel, int i, int i1) {
super.onChannelClosed(channel, i, i1);
Log.d(TAG, "receiveFileFromHandheld " + channel.getNodeId() + " onChannelClosed ");
if (!mClosed){
// failure ...
}
}
#Override
public void onInputClosed(#NonNull ChannelClient.Channel channel, int i, int i1) {
super.onInputClosed(channel, i, i1);
Log.d(TAG, "receiveFileFromHandheld " + channel.getNodeId() + " onInputClosed ");
long fs = new File(receivedFileName).length();
Log.d(TAG, "receiveFileFromHandheld got " + receivedFileName +
" size:" + fs / 1000000 + "Mb");
cc.unregisterChannelCallback(this);
mClosed = true;
// success !
}
};
try {
Log.d(TAG, "receiveFileFromHandheld register callback");
Tasks.await(cc.registerChannelCallback(c, ccb));
Log.d(TAG, "receiveFileFromHandheld receiving file " + receivedFileName);
Tasks.await(cc.receiveFile(c, Uri.fromFile(new File(receivedFileName)), false));
// completion is indicated by onInputClosed
} catch (Exception e) {
Log.w(TAG, "receiveFileFromHandheld exception " + e.getMessage());
cc.unregisterChannelCallback(ccb);
// failure ...
}
}
}
).start();
}

Resources