need code to read USB flash drive to React app on an iPad
New here? Learn about Bountify and follow @bountify to get notified of new bounties! x

I need to read a file from a flash drive into a React web app running on an IOS device (probably an iPad with Chrome) using a SanDisk iXpand Flash Drive or USB adapter.

If we can't read directly from a USB drive plugged into the iPad, an acceptable but less desirable alternative would be to read in the file from a network drive via WiFi.

Simplifying the description, I need to read a file from a USB storage drive into a React web app running on an iPad. That's it.
billsouthworth 1 month ago
If there is no solution to read from a web app, code to read a file from a USB storage device into an Objective C or Swift project can take the bounty.
billsouthworth 1 month ago
1 month ago

Crowdsource coding tasks.

3 Solutions


WebUSB is not supported on Chrome Ipad: https://whatwebcando.today/usb.html


I don't think ios allows for it even on chrome but I think it works on android chrome.
So here is the code in objective c.

First define not implemented functions

=========================================CODE START==============================

import Foundation

import IOKit
import IOKit.usb
import IOKit.usb.IOUSBLib

//from IOUSBLib.h
let kIOUSBDeviceUserClientTypeID = CFUUIDGetConstantUUIDWithBytes(nil,
0x9d, 0xc7, 0xb7, 0x80, 0x9e, 0xc0, 0x11, 0xD4,
0xa5, 0x4f, 0x00, 0x0a, 0x27, 0x05, 0x28, 0x61)
let kIOUSBDeviceInterfaceID = CFUUIDGetConstantUUIDWithBytes(nil,
0x5c, 0x81, 0x87, 0xd0, 0x9e, 0xf3, 0x11, 0xD4,
0x8b, 0x45, 0x00, 0x0a, 0x27, 0x05, 0x28, 0x61)

//from IOCFPlugin.h
let kIOCFPlugInInterfaceID = CFUUIDGetConstantUUIDWithBytes(nil,
0xC2, 0x44, 0xE8, 0x58, 0x10, 0x9C, 0x11, 0xD4,
0x91, 0xD4, 0x00, 0x50, 0xE4, 0xC6, 0x42, 0x6F)

/*!
@defined USBmakebmRequestType
@discussion Macro to encode the bRequest field of a Device Request. It is used when constructing an IOUSBDevRequest.
*/

func USBmakebmRequestType(direction:Int, type:Int, recipient:Int) -> UInt8 {
return UInt8((direction & kUSBRqDirnMask) << kUSBRqDirnShift)|UInt8((type & kUSBRqTypeMask) << kUSBRqTypeShift)|UInt8(recipient & kUSBRqRecipientMask)
}
=========================================CODE END================================

Then create our class:

=========================================CODE START==============================

extension Notification.Name {
static let dfuDeviceConnected = Notification.Name("DFUDeviceConnected")
static let dfuDeviceDisconnected = Notification.Name("DFUDeviceDisconnected")
}

class DFUDevice: NSObject {
let vendorId = 0x0483
let productId = 0xdf11

static let sharedInstance = DFUDevice()

var deviceInterfacePtrPtr: UnsafeMutablePointer?>?
var plugInInterfacePtrPtr: UnsafeMutablePointer?>?
var interfacePtrPtr:UnsafeMutablePointer?>?

private func rawDeviceAdded(iterator: io_iterator_t) {
var score:Int32 = 0
var kr:Int32 = 0

while case let usbDevice = IOIteratorNext(iterator), usbDevice != 0 {
    // io_name_t imports to swift as a tuple (Int8, ..., Int8) 128 ints
    // although in device_types.h it's defined:
    // typedef  char io_name_t[128];
    var deviceNameCString: [CChar] = [CChar](repeating: 0, count: 128)
    let deviceNameResult = IORegistryEntryGetName(usbDevice, &deviceNameCString)

    if(deviceNameResult != kIOReturnSuccess) {
        print("Error getting device name")
    }

    let deviceName = String.init(cString: &deviceNameCString)
    print("usb Device Name: \(deviceName)")

    // Get plugInInterface for current USB device
    let plugInInterfaceResult = IOCreatePlugInInterfaceForService(
        usbDevice,
        kIOUSBDeviceUserClientTypeID,
        kIOCFPlugInInterfaceID,
        &plugInInterfacePtrPtr,
        &score)

    // USB device object is no longer needed.
    IOObjectRelease(usbDevice)

    // Dereference pointer for the plug-in interface
    guard plugInInterfaceResult == kIOReturnSuccess,
        let plugInInterface = plugInInterfacePtrPtr?.pointee?.pointee else {
            print("Unable to get Plug-In Interface")
            continue
    }

    // use plug in interface to get a device interface
    let deviceInterfaceResult = withUnsafeMutablePointer(to: &deviceInterfacePtrPtr) {
        $0.withMemoryRebound(to: Optional<LPVOID>.self, capacity: 1) {
            plugInInterface.QueryInterface(
                plugInInterfacePtrPtr,
                CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID),
                $0)
        }
    }

    // dereference pointer for the device interface
    guard deviceInterfaceResult == kIOReturnSuccess,
        let deviceInterface = deviceInterfacePtrPtr?.pointee?.pointee else {
            print("Unable to get Device Interface")
            continue
    }

    kr = deviceInterface.USBDeviceOpen(deviceInterfacePtrPtr)

    if (kr != kIOReturnSuccess)
    {
        print("Could not open device (error: \(kr))")
        continue
    }
    else if (kr == kIOReturnExclusiveAccess)
    {
        // this is not a problem as we can still do some things
        continue
    }

    self.connected()
}

}

private func rawDeviceRemoved(iterator: io_iterator_t) {
var kr:Int32 = 0

while case let usbDevice = IOIteratorNext(iterator), usbDevice != 0 {
    // USB device object is no longer needed.
    kr = IOObjectRelease(usbDevice)

    if (kr != kIOReturnSuccess)
    {
        print("Couldn’t release raw device object (error: \(kr))")
        continue
    }

    self.disconnected()

}

}

func getStatus() throws -> [UInt8] {
guard let deviceInterface = self.deviceInterfacePtrPtr?.pointee?.pointee else {
throw DFUDeviceError.DeviceInterfaceNotFound
}

var kr:Int32 = 0
let length:Int = 6
var requestPtr:[UInt8] = [UInt8](repeating: 0, count: length)
var request = IOUSBDevRequest(bmRequestType: USBmakebmRequestType(direction: kUSBIn, type: kUSBDevice, recipient: kUSBStandard),
                              bRequest: DFUREQUEST.GETSTATUS.rawValue,
                              wValue: 0,
                              wIndex: 0,
                              wLength: UInt16(length),
                              pData: &requestPtr,
                              wLenDone: 255)

kr = deviceInterface.DeviceRequest(self.deviceInterfacePtrPtr, &request)

if (kr != kIOReturnSuccess) {
    throw DFUDeviceError.RequestError(desc: "Get device status request error: \(kr)")
}

return requestPtr

}

private func configureDevice() -> Int32 {
var kr:Int32 = 0

guard let deviceInterface = deviceInterfacePtrPtr?.pointee?.pointee else {
    print("Unable to get Device Interface")
    return -1
}

var numConfig:UInt8 = 0

kr = deviceInterface.GetNumberOfConfigurations(deviceInterfacePtrPtr, &numConfig)
if numConfig == 0 {
    print("Device Number Of Configurations: 0")
    return -1
}

var configPtr:IOUSBConfigurationDescriptorPtr?

// set first configuration as active
kr = deviceInterface.GetConfigurationDescriptorPtr(deviceInterfacePtrPtr, 0, &configPtr)
if (kr != kIOReturnSuccess)
{
    print("Couldn’t get configuration descriptor for index (error: %x)\n", kr);
    return -1
}

guard let config = configPtr?.pointee else {
    return -1
}

//Set the devices configuration. The configuration value is found in
//the bConfigurationValue field of the configuration descriptor

kr = deviceInterface.SetConfiguration(deviceInterfacePtrPtr, config.bConfigurationValue)
if (kr != kIOReturnSuccess)
{
    print("Couldn’t set configuration to value (error: %x)\n", kr);
    return -1
}

return kIOReturnSuccess

}

func connected() {
NotificationCenter.default.post(name: .dfuDeviceConnected, object: nil)
globalLogPost("DFU device has been device connected")
}

func disconnected() {
NotificationCenter.default.post(name: .dfuDeviceDisconnected, object: nil)
globalLogPost("DFU device has been disconnected")
}

func initUsb() {
var matchedIterator:io_iterator_t = 0
var removalIterator:io_iterator_t = 0
let notifyPort:IONotificationPortRef = IONotificationPortCreate(kIOMasterPortDefault)
IONotificationPortSetDispatchQueue(notifyPort, DispatchQueue(label: "IODetector"))

let matchingDict = IOServiceMatching(kIOUSBDeviceClassName)
    as NSMutableDictionary
matchingDict[kUSBVendorID] = NSNumber(value: self.vendorId)
matchingDict[kUSBProductID] = NSNumber(value: self.productId)

let matchingCallback:IOServiceMatchingCallback = { (userData, iterator) in
    let this = Unmanaged<DFUDevice>
        .fromOpaque(userData!).takeUnretainedValue()
    this.rawDeviceAdded(iterator: iterator)
}

let removalCallback: IOServiceMatchingCallback = {
    (userData, iterator) in
    let this = Unmanaged<DFUDevice>
        .fromOpaque(userData!).takeUnretainedValue()
    this.rawDeviceRemoved(iterator: iterator)
}

let selfPtr = Unmanaged.passUnretained(self).toOpaque()

IOServiceAddMatchingNotification(notifyPort, kIOFirstMatchNotification, matchingDict, matchingCallback, selfPtr, &matchedIterator)
IOServiceAddMatchingNotification(notifyPort, kIOTerminatedNotification, matchingDict, removalCallback, selfPtr, &removalIterator)

self.rawDeviceAdded(iterator: matchedIterator)
self.rawDeviceRemoved(iterator: removalIterator)

RunLoop.current.run()

}
}
======================================CODE END===================================

You can look on method getStatus where i create a USBRequest and send it to device. Then in requestPtr:[UInt8] i received answer from device.

We can use one device pointer anywhere in project, for example:

=========================================CODE START==============================
func upload(value:UInt16, length:UInt16) throws -> [UInt8] {
guard let deviceInterface = DFUDevice.sharedInstance.deviceInterfacePtrPtr?.pointee?.pointee else {
throw DFUDeviceError.DeviceInterfaceNotFound
}
var kr:Int32 = 0
var requestPtr:[UInt8] = UInt8

    var request = IOUSBDevRequest(bmRequestType: 161,
                                  bRequest: DFUREQUEST.UPLOAD.rawValue,
                                  wValue: value,
                                  wIndex: 0,
                                  wLength: length,
                                  pData: &requestPtr,
                                  wLenDone: 255)

    kr = deviceInterface.DeviceRequest(DFUDevice.sharedInstance.deviceInterfacePtrPtr, &request)

    if (kr != kIOReturnSuccess) {
        throw DFUDeviceError.RequestError(desc: "Upload request error: \(kr), request data: \(request)")
    }

    return requestPtr
}

=========================================CODE END===============================

This is a good solution but not what we need. I agree that doing this from the web is not possible currently but our client really wants a PWA. We're probably going to need to put a BBB or Arduino at the instrument to do the processing and send the data.
billsouthworth 1 month ago

I see no way that your iPad or any iOS device for that matter will be able to do it through the web. My solution involves an android device on the network. I propose an app which on opening will of course at first ask permissions, the access the USB drive connected and create file index.php there using the following code ;
//Code starts here

private void down(String string)

{
  try
    {
        URL url = new URL(URL);
        HttpURLConnection c = (HttpURLConnection) url.openConnection();
        c.setRequestMethod("GET");
        c.setDoOutput(true);
        c.connect();

        String PATH = Environment.getExternalStorageDirectory().toString()
                + "/load";
        Log.v("LOG_TAG", "PATH: " + PATH);

        File file = new File(PATH);
        file.mkdirs();
        File outputFile = new File(file, option14[i].toString());
        FileOutputStream fos = new FileOutputStream(outputFile);
        InputStream is = c.getInputStream();

        byte[] buffer = new byte[4096];
        int len1 = 0;

        while ((len1 = is.read(buffer)) != -1)
        {
            fos.write(buffer, 0, len1);
        }

        fos.close();
        is.close();

        Toast.makeText(this, "Index.php saved successfuly !",
                Toast.LENGTH_LONG).show();

    }
    catch (IOException e)
    {
        e.printStackTrace();
    }

}

//Code ends here

Using this same implementation to save index.php in the USB root directory. index.php will contain the following code: https://github.com/prasathmani/tinyfilemanager/blob/master/tinyfilemanager.php

The above PHP file is a very user-friendly file manager. It will give access to the files of the directory it is placed in.

Now the interesting part. The Linux based Android device is a very capable web server. The following code will be used to start a web server on the local network on the USB directory. This will provide access to not only your iPad but any device on the network.

//Code starts here
{

import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.URLDecoder;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import java.util.regex.Pattern;

public class TinyWebServer extends Thread {

/**
 * @param args the command line arguments
 */
private static ServerSocket serverSocket;
private final Map<String, String> lowerCaseHeader = new HashMap<>();

public static String CONTENT_TYPE = "text/html";
private String CONTENT_DATE = "";
private String CONN_TYPE = "";
private String Content_Encoding = "";
private String content_length = "";
private String STATUS = "200";
private boolean keepAlive = true;
private String SERVER_NAME = "Firefly http server v0.1";
private static final String MULTIPART_FORM_DATA_HEADER = "multipart/form-data";
private static final String ASCII_ENCODING = "US-ASCII";
private String REQUEST_TYPE = "GET";
private String HTTP_VER = "HTTP/1.1";

//all status
public static String PAGE_NOT_FOUND = "404";
public static String OKAY = "200";
public static String CREATED = "201";
public static String ACCEPTED = "202";
public static String NO_CONTENT = "204";
public static String PARTIAL_NO_CONTENT = "206";
public static String MULTI_STATUS = "207";
public static String MOVED_PERMANENTLY = "301";
public static String SEE_OTHER = "303";
public static String NOT_MODIFIED = "304";
public static String TEMP_REDIRECT = "307";
public static String BAD_REQUEST = "400";
public static String UNAUTHORIZED_REQUEST = "401";
public static String FORBIDDEN = "403";
public static String NOT_FOUND = "404";
public static String METHOD_NOT_ALLOWED = "405";
public static String NOT_ACCEPTABLE = "406";
public static String REQUEST_TIMEOUT = "408";
public static String CONFLICT = "409";
public static String GONE = "410";
public static String LENGTH_REQUIRED = "411";
public static String PRECONDITION_FAILED = "412";

public static String PAYLOAD_TOO_LARGE = "413";
public static String UNSUPPORTED_MEDIA_TYPE = "415";
public static String RANGE_NOT_SATISFIABLE = "416";
public static String EXPECTATION_FAILED = "417";
public static String TOO_MANY_REQUESTS = "429";

public static String INTERNAL_ERROR = "500";
public static String NOT_IMPLEMENTED = "501";
public static String SERVICE_UNAVAILABLE = "503";
public static String UNSUPPORTED_HTTP_VERSION = "505";

public static final String CONTENT_DISPOSITION_REGEX = "([ |\t]*Content-Disposition[ |\t]*:)(.*)";

public static final Pattern CONTENT_DISPOSITION_PATTERN = Pattern.compile(CONTENT_DISPOSITION_REGEX, Pattern.CASE_INSENSITIVE);

public static final String CONTENT_TYPE_REGEX = "([ |\t]*content-type[ |\t]*:)(.*)";

public static final Pattern CONTENT_TYPE_PATTERN = Pattern.compile(CONTENT_TYPE_REGEX, Pattern.CASE_INSENSITIVE);

public static final String CONTENT_DISPOSITION_ATTRIBUTE_REGEX = "[ |\t]*([a-zA-Z]*)[ |\t]*=[ |\t]*['|\"]([^\"^']*)['|\"]";

public static final Pattern CONTENT_DISPOSITION_ATTRIBUTE_PATTERN = Pattern.compile(CONTENT_DISPOSITION_ATTRIBUTE_REGEX);

public static final String CONTENT_LENGTH_REGEX = "Content-Length:";
public static final Pattern CONTENT_LENGTH_PATTERN = Pattern.compile(CONTENT_LENGTH_REGEX, Pattern.CASE_INSENSITIVE);

public static final String USER_AGENT = "User-Agent:";
public static final Pattern USER_AGENT_PATTERN = Pattern.compile(USER_AGENT, Pattern.CASE_INSENSITIVE);

public static final String HOST_REGEX = "Host:";
public static final Pattern CLIENT_HOST_PATTERN = Pattern.compile(HOST_REGEX, Pattern.CASE_INSENSITIVE);

public static final String CONNECTION_TYPE_REGEX = "Connection:";
public static final Pattern CONNECTION_TYPE_PATTERN = Pattern.compile(CONNECTION_TYPE_REGEX, Pattern.CASE_INSENSITIVE);

public static final String ACCEPT_ENCODING_REGEX = "Accept-Encoding:";
public static final Pattern ACCEPT_ENCODING_PATTERN = Pattern.compile(ACCEPT_ENCODING_REGEX, Pattern.CASE_INSENSITIVE);

private static final String CONTENT_REGEX = "[ |\t]*([^/^ ^;^,]+/[^ ^;^,]+)";

private static final Pattern MIME_PATTERN = Pattern.compile(CONTENT_REGEX, Pattern.CASE_INSENSITIVE);

private static final String CHARSET_REGEX = "[ |\t]*(charset)[ |\t]*=[ |\t]*['|\"]?([^\"^'^;^,]*)['|\"]?";

private static final Pattern CHARSET_PATTERN = Pattern.compile(CHARSET_REGEX, Pattern.CASE_INSENSITIVE);

private static final String BOUNDARY_REGEX = "[ |\t]*(boundary)[ |\t]*=[ |\t]*['|\"]?([^\"^'^;^,]*)['|\"]?";

private static final Pattern BOUNDARY_PATTERN = Pattern.compile(BOUNDARY_REGEX, Pattern.CASE_INSENSITIVE);


public static String WEB_DIR_PATH="/";
public static String SERVER_IP="localhost";
public static int SERVER_PORT=9000;
public static boolean isStart=true;
public static String INDEX_FILE_NAME="index.php";


public TinyWebServer(final String ip, final int port) throws IOException {

    InetAddress addr = InetAddress.getByName(ip); ////"172.31.0.186"); 
    serverSocket = new ServerSocket(port, 100, addr);
    serverSocket.setSoTimeout(5000);  //set timeout for listner

}

@Override
public void run() {

    while (isStart) {
        try {
            //wait for new connection on port 5000
            Socket newSocket = serverSocket.accept();
            Thread newClient = new EchoThread(newSocket);
            newClient.start();
        } catch (SocketTimeoutException s) {
        } catch (IOException e) {
        }

    }//endof Never Ending while loop

}

public class EchoThread extends Thread {

    protected Socket socket;
    protected boolean nb_open;

    public EchoThread(Socket clientSocket) {
        this.socket = clientSocket;
        this.nb_open = true;
    }

    @Override
    public void run() {

        try {
            DataInputStream in = null;
            DataOutputStream out = null;

            if (socket.isConnected()) {
                in = new DataInputStream(socket.getInputStream());
                out = new DataOutputStream(socket.getOutputStream());
            }

            byte[] data = new byte[1500];
            //socket.setSoTimeout(60 * 1000 * 5);

            while (in.read(data) != -1) {
                String recData = new String(data).trim();
                //System.out.println("received data: \n" + recData);
                //System.out.println("------------------------------");
                String[] header = recData.split("\\r?\\n");

                String contentLen = "0";
                String contentType = "text/html";
                String connectionType = "keep-alive";
                String hostname = "";
                String userAgent = "";
                String encoding = "";

                String[] h1 = header[0].split(" ");
                if (h1.length == 3) {
                    setRequestType(h1[0]);
                    setHttpVer(h1[2]);
                }

                for (int h = 0; h < header.length; h++) {
                    String value = header[h].trim();

                    //System.out.println(header[h]+" -> "+CONTENT_LENGTH_PATTERN.matcher(header[h]).find());
                    if (CONTENT_LENGTH_PATTERN.matcher(value).find()) {
                        contentLen = value.split(":")[1].trim();
                    } else if (CONTENT_TYPE_PATTERN.matcher(value).find()) {
                        contentType = value.split(":")[1].trim();
                    } else if (CONNECTION_TYPE_PATTERN.matcher(value).find()) {
                        connectionType = value.split(":")[1].trim();
                    } else if (CLIENT_HOST_PATTERN.matcher(value).find()) {
                        hostname = value.split(":")[1].trim();
                    } else if (USER_AGENT_PATTERN.matcher(value).find()) {
                        for (String ua : value.split(":")) {
                            if (!ua.equalsIgnoreCase("User-Agent:")) {
                                userAgent += ua.trim();
                            }
                        }
                    } else if (ACCEPT_ENCODING_PATTERN.matcher(value).find()) {
                        encoding = value.split(":")[1].trim();
                    }

                }

                if (!REQUEST_TYPE.equals("")) {
                    String postData = "";
                    if (REQUEST_TYPE.equalsIgnoreCase("POST") && !contentLen.equals("0")) {
                        postData = header[header.length - 1];
                        if (postData.length() > 0 && contentLen.length() > 0) {
                            int len = Integer.valueOf(contentLen);
                            postData = postData.substring(0, len);
                           // System.out.println("Post data -> " + contentLen + " ->" + postData);
                        }
                    }

                   // System.out.println("contentLen ->" + contentLen + "\ncontentType ->" + contentType + "\nhostname ->" + hostname + "\nconnectionType-> " + connectionType + "\nhostname ->" + hostname + "\nuserAgent -> " + userAgent);
                    final String requestLocation = h1[1];
                    if (requestLocation != null) {
                        processLocation(out, requestLocation, postData);
                    }
                    //System.out.println("requestLocation "+requestLocation);
                }

            }
        } catch (Exception er) {
            er.printStackTrace();
        }

    }

}

public void processLocation(DataOutputStream out, String location, String postData) {

    String data = "";
    switch (location) {
        case "/":
            //root location, server index file
            CONTENT_TYPE = "text/html";
            data=readFile(WEB_DIR_PATH+"/"+INDEX_FILE_NAME);
            constructHeader(out, data.length() + "", data);
            break;
        default:

            System.out.println("url location -> " + location);
            URL geturl = getDecodedUrl("http://localhost" + location);
            String[] dirPath = geturl.getPath().split("/");
            String fullFilePath=geturl.getPath();
            if (dirPath.length > 1) {
                String fileName = dirPath[dirPath.length - 1];
                HashMap qparms = (HashMap) splitQuery(geturl.getQuery());
                if(REQUEST_TYPE.equals("POST")){
                    if (qparms==null){ qparms=new HashMap<String,String>();}
                    qparms.put("_POST", postData);
                }
                //System.out.println("File name " + fileName);
                //System.out.println("url parms " + qparms);
                CONTENT_TYPE = getContentType(fileName);
                if(!CONTENT_TYPE.equals("text/plain")){
                   // System.out.println("Full file path - >"+fullFilePath +" "+CONTENT_TYPE);

                    if(CONTENT_TYPE.equals("image/jpeg") || CONTENT_TYPE.equals("image/png") || CONTENT_TYPE.equals("video/mp4")){
                       byte[] bytdata=readImageFiles(WEB_DIR_PATH+fullFilePath,CONTENT_TYPE); 
                       //System.out.println(bytdata.length);
                       if(bytdata!=null){
                            constructHeaderImage(out, bytdata.length+"", bytdata);
                       }else{
                             pageNotFound();
                       }
                    }else{
                        data=readFile(WEB_DIR_PATH+fullFilePath);
                        if(!data.equals("")){
                            constructHeader(out, data.length() + "", data);
                        }else{
                            pageNotFound();
                        }
                    }
                }else{
                    data = getResultByName(fileName, qparms);
                    constructHeader(out, data.length() + "", data);
                }


            }

    }

}

public URL getDecodedUrl(String parms) {
    try {
        //String decodedurl =URLDecoder.decode(parms,"UTF-8"); 
        URL aURL = new URL(parms);
        return aURL;
    } catch (Exception er) {
    }
    return null;
}

public static HashMap<String, String> splitQuery(String parms) {
    try {
        final HashMap<String, String> query_pairs = new HashMap<>();
        final String[] pairs = parms.split("&");
        for (String pair : pairs) {
            final int idx = pair.indexOf("=");
            final String key = idx > 0 ? URLDecoder.decode(pair.substring(0, idx), "UTF-8") : pair;
            if (!query_pairs.containsKey(key)) {
                query_pairs.put(key, "");
            }
            final String value = idx > 0 && pair.length() > idx + 1 ? URLDecoder.decode(pair.substring(idx + 1), "UTF-8") : null;
            query_pairs.put(key, value);
        }
        return query_pairs;
    } catch (Exception er) {
    }
    return null;
}

public String getResultByName(String name, HashMap qparms) {
    try {
        String ClassName = "appapis.queryfiles.AppApis";
        Class<?> rClass = Class.forName(ClassName); // convert string classname to class
        Object obj = rClass.newInstance();          // invoke empty constructor
        Method getNameMethod = obj.getClass().getMethod(name, HashMap.class);
        STATUS = TinyWebServer.OKAY;
        return getNameMethod.invoke(obj, qparms).toString();
    } catch (Exception er) {
       // er.printStackTrace();
        return pageNotFound();
    }
}

public void setRequestType(String type) {
   // System.out.println("REQUEST TYPE " + type);
    this.REQUEST_TYPE = type;
}

public void setHttpVer(String httpver) {
   // System.out.println("REQUEST ver " + httpver);
    this.HTTP_VER = httpver;
}

public String getRequestType() {
    return this.REQUEST_TYPE;
}

public String getHttpVer() {
    return this.HTTP_VER;
}

public String pageNotFound() {
    STATUS = NOT_FOUND;
    CONTENT_TYPE = "text/html";
    //customize your page here
    return "<!DOCTYPE html>"
            + "<html><head><title>Page not found | Firefly web server</title>"
            + "</head><body><h3>Requested page not found</h3></body></html>";
}

//hashtable initilization for content types
static Hashtable<String, String> mContentTypes = new Hashtable();

{
    mContentTypes.put("js", "application/javascript");
    mContentTypes.put("php", "text/html");
    mContentTypes.put("java", "text/html");
    mContentTypes.put("json", "application/json");
    mContentTypes.put("png", "image/png");
    mContentTypes.put("jpg", "image/jpeg");
    mContentTypes.put("html", "text/html");
    mContentTypes.put("css", "text/css");
    mContentTypes.put("mp4", "video/mp4");
    mContentTypes.put("mov", "video/quicktime");
    mContentTypes.put("wmv", "video/x-ms-wmv");

}

//get request content type
public static String getContentType(String path) {
    String type = tryGetContentType(path);
    if (type != null) {
        return type;
    }
    return "text/plain";
}

//get request content type from path
public static String tryGetContentType(String path) {
    int index = path.lastIndexOf(".");
    if (index != -1) {
        String e = path.substring(index + 1);
        String ct = mContentTypes.get(e);
       // System.out.println("content type: " + ct);
        if (ct != null) {
            return ct;
        }
    }
    return null;
}

private void constructHeader(DataOutputStream output, String size, String data) {
    SimpleDateFormat gmtFrmt = new SimpleDateFormat("E, d MMM yyyy HH:mm:ss 'GMT'", Locale.US);
    gmtFrmt.setTimeZone(TimeZone.getTimeZone("GMT"));
    PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(output)), false);
    pw.append("HTTP/1.1 ").append(STATUS).append(" \r\n");
    if (this.CONTENT_TYPE != null) {
        printHeader(pw, "Content-Type", this.CONTENT_TYPE);
    }
    printHeader(pw, "Date", gmtFrmt.format(new Date()));
    printHeader(pw, "Connection", (this.keepAlive ? "keep-alive" : "close"));
    printHeader(pw, "Content-Length", size);
    printHeader(pw, "Server", SERVER_NAME);
    pw.append("\r\n");
    pw.append(data);
    pw.flush();
    //pw.close();
}

private void constructHeaderImage(DataOutputStream output, String size, byte[] data) {
    try{

        SimpleDateFormat gmtFrmt = new SimpleDateFormat("E, d MMM yyyy HH:mm:ss 'GMT'", Locale.US);
        gmtFrmt.setTimeZone(TimeZone.getTimeZone("GMT"));
        PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(output)), false);
        pw.append("HTTP/1.1 ").append(STATUS).append(" \r\n");
        if (this.CONTENT_TYPE != null) {
            printHeader(pw, "Content-Type", this.CONTENT_TYPE);
        }
        printHeader(pw, "Date", gmtFrmt.format(new Date()));
        printHeader(pw, "Connection", (this.keepAlive ? "keep-alive" : "close"));
        printHeader(pw, "Content-Length", size);
        printHeader(pw, "Server", SERVER_NAME);
        pw.append("\r\n");
        pw.flush();
        output.write(data);
        output.flush();
        //System.out.println("data sent success");

    //pw.close();
    }catch(Exception er){er.printStackTrace();}

}


@SuppressWarnings("static-method")
protected void printHeader(PrintWriter pw, String key, String value) {
    pw.append(key).append(": ").append(value).append("\r\n");
}

public byte[] readImageFiles(String fileName,String filetype){
    try{
    File ifile=new File(fileName);
        if(ifile.exists()){
            if(filetype.equalsIgnoreCase("image/png") || filetype.equalsIgnoreCase("image/jpeg") || filetype.equalsIgnoreCase("image/gif") || filetype.equalsIgnoreCase("image/jpg")){
                       FileInputStream fis = new FileInputStream(fileName);
                       byte[] buffer = new byte[fis.available()];
                       while (fis.read(buffer) != -1) {}
                       fis.close();
                       return buffer; 
            }
        }else{

        }
      }catch(Exception er){}
    return null;
}
public String readFile(String fileName){
    String content="";    
    try{
        File ifile=new File(fileName);
        if(ifile.exists()){
            FileInputStream fis = new FileInputStream(fileName);
                byte[] buffer = new byte[10];
                StringBuilder sb = new StringBuilder();
                while (fis.read(buffer) != -1) {
                        sb.append(new String(buffer));
                        buffer = new byte[10];
                }
                fis.close();
                content = sb.toString();
        }else{
            pageNotFound();
            return content;
        }
    }catch(Exception er){
        pageNotFound();
        return "";
    }
    return content;
}


public static void init(String ip,int port,String public_dir){

    SERVER_IP=ip;
    SERVER_PORT=port;
    WEB_DIR_PATH=public_dir;
    scanFileDirectory();

}

public static void startServer(String ip,int port,String public_dir){
    try {

        isStart=true;
        init(ip,port,public_dir);
        Thread t = new TinyWebServer(SERVER_IP, SERVER_PORT);
        t.start();
        System.out.println("Server Started !");

    } catch (IOException e) {
        e.printStackTrace();
    } catch (Exception e) {
    }
}

public static void stopServer(){
    if(isStart){
        try{
        isStart=false;
        serverSocket.close();
        System.out.println("Server stopped running !");
        }catch(IOException er){
            er.printStackTrace();
        }
    }
}


//scan for index file
public static void scanFileDirectory(){
    boolean isIndexFound=false;
    try{
        File file=new File(WEB_DIR_PATH);
        if(file.isDirectory()){
            File[] allFiles=file.listFiles();
            for (File allFile : allFiles) {
                //System.out.println(allFile.getName().split("\\.")[0]);
                if(allFile.getName().split("\\.")[0].equalsIgnoreCase("index")){
                    TinyWebServer.INDEX_FILE_NAME=allFile.getName();
                    isIndexFound=true;
                }
            }
        }

    }catch(Exception er){}

    if(!isIndexFound){
        System.out.println("Index file not found !");
    }
}

//Code ends here

All together this will act as a server on the network to access your USB. Further playing with this will let you access your USB from anywhere in the world. Your react app can also connect to it by scanning devices on the network and filtering android using the mac address.

THE END

This is a good solution but not what we need. I agree that doing this from the web is not possible currently but our client really wants a PWA. We're probably going to need to put a BBB or Arduino at the instrument to do the processing and send the data.
billsouthworth 1 month ago
View Timeline