3

I'm hitting an API Endpoint with a Kotlin Android app. In this API call I'm returning a byte Array. What I would like to see is a way to convert the byte array to a pdf file and save it in the phones download folder.

    private suspend fun getIDCard(sessionID: String?, carriermemid: String?): ByteArray? {
        var results: String = ""

        val postData = "{\"sessionid\": \" $sessionID\"}"
        val outputStreamWriter: OutputStreamWriter

        var byteArray: ByteArray? = null
        val url: URL = URL(idURL + carriermemid)
        val conn: HttpURLConnection = url.openConnection() as HttpURLConnection

        try {
            conn.setRequestProperty(apiConfig.typeKey, apiConfig.typeValueJSON)
            conn.setRequestProperty("Accept", "application/json")
            conn.setRequestProperty("sessionid", sessionID)
            conn.requestMethod = apiConfig.methodGet


            val responseCode: Int = conn.responseCode
            println("Response Code :: $responseCode")
            //returning 404
            return if (responseCode == HttpURLConnection.HTTP_OK) {// connection ok
                var out: ByteArrayOutputStream? = ByteArrayOutputStream()
                val `in`: InputStream = conn.inputStream
                var bytesRead: Int
                val buffer = ByteArray(1024)
                while (`in`.read(buffer).also { bytesRead = it } > 0) {
                    out!!.write(buffer, 0, bytesRead)
                }
                out!!.close()
                byteArray = out.toByteArray()
                return byteArray

            } else {
                return byteArray
            }

        } catch (ex: Exception) {
            ex.printStackTrace()
        } finally {
            conn.disconnect()
        }
        return byteArray
    }
yams
  • 942
  • 6
  • 27
  • 60
  • 4
    `convert the byte array to a pdf file and save it in the phones download folder.` What is in the byte array that can be converted to a pdf? I think the byte array will contain the pdf and all you have to do is saving the bytes in the array to file and you are done. – blackapps May 19 '20 at 06:55
  • @blackapps the byte array does contain the pdf. As far as how I'm looking for examples. :facepalm: – yams May 19 '20 at 13:19
  • 1
    So basically your question is: How can I store a file in the global download folder? – tynn May 22 '20 at 09:31
  • @tynn convert a byte array to a file to store in the global download. – yams May 22 '20 at 19:18

3 Answers3

11

This will get the Android download directory and write the byte array as a PDF file (assuming the byte array contains a PDF). Change File.createTempFile to any file you like (you don't need to create a temporary file):

fun writeBytesAsPdf(bytes : ByteArray) {
   val path = requireContext().getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)
    var file = File.createTempFile("my_file",".pdf", path)
    var os = FileOutputStream(file);
    os.write(bytes);
    os.close();
}

You will also have to add android.permission.WRITE_EXTERNAL_STORAGE to your manifest.

user2199860
  • 788
  • 4
  • 14
  • To avoid needing the context in the viewModel, you could get the path in the fragment/activity and save it in the viewModel, it should be constant for a particular user. – user2199860 May 28 '20 at 15:25
  • You also don't need to use createTemporaryFile, that may or may not work in the Downloads folder. You can just use (or similar): File(path, "my_file.pdf") – user2199860 May 28 '20 at 15:57
  • Yeah the manifest is updated let me try getting rid of Create temp file – yams May 28 '20 at 16:59
  • That didn't work either. Maybe due to permissions or the byte array I'll take a look in a bit – yams May 29 '20 at 14:27
  • that wasn't what fixed it at all. See the accepted answer. – yams Apr 21 '21 at 15:55
1

Looking at How to download PDF file with Retrofit and Kotlin coroutines?, you can use:

private const val BUFFER_SIZE = 4 * 1024

private fun copyStreamToFile(inputStream: InputStream, outputFile: File) {
    inputStream.use { input ->
        val outputStream = FileOutputStream(outputFile)
        outputStream.use { output ->
            val buffer = ByteArray(BUFFER_SIZE)
            while (true) {
                val byteCount = input.read(buffer)
                if (byteCount < 0) break
                output.write(buffer, 0, byteCount)
            }
            output.flush()
        }
    }
}

or

private fun InputStream.saveToFile(file: String) = use { input ->
    File(file).outputStream().use { output ->
        input.copyTo(output)
    }
}

Also you should create the file.

private fun createFile(context: Context, name: String): File? {
    val storageDir = context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)?.path
    var file = File("$storageDir/$name.pdf")
    return storageDir?.let { file }
}
CoolMind
  • 26,736
  • 15
  • 188
  • 224
-1

So this ended up being what fixed this. Almost forgot to post this. It was the resolveinfo list and the way I granted permission that got me through but here goes

                    try {
                        val pdfbyte = viewModel.getPDFImage()
                        val path =
                            requireContext().getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)
                                .toString() + File.separator
                        val fileName = "my_idcard.pdf"
                        val storageDir =
                            requireContext().getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)?.path
                        var file = File("$storageDir/$fileName")
                        var os = FileOutputStream(file);
                        os.write(pdfbyte);
                        os.close();
                        val intent = Intent(Intent.ACTION_VIEW)
                        val uri: Uri = FileProvider.getUriForFile(
                            requireContext(),
                            BuildConfig.APPLICATION_ID + ".provider",
                            file
                        )
                        intent.setDataAndType(uri, "application/pdf")
                        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
                        intent.flags = Intent.FLAG_ACTIVITY_NO_HISTORY
                        val resInfoList: List<ResolveInfo> = requireActivity().getPackageManager()
                            .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY)

                        for (resolveInfo in resInfoList) {
                            val packageName = resolveInfo.activityInfo.packageName
                            requireActivity().grantUriPermission(
                                packageName,
                                uri,
                                Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION
                            )
                        }
                        startActivity(intent)
                    } catch (e: ActivityNotFoundException) {
                        println("*************************NO PDF**************************")
                    }
yams
  • 942
  • 6
  • 27
  • 60