In the program that I am writing, I take the uri of the location to save the zip file from a user. Then, I try to zip the files and folders using zip4j library and outpustream in Android. I modified the code in this Stackoverflow answer and used zip4j instead. My modified code produces the zip file, however it is corrupted.
this is my code written in Kotlin:
class ZipBuilder {
private fun buildZipParameters(compressionMethod: CompressionMethod, compressionLevel: CompressionLevel,
encrypt: Boolean,
encryptionMethod: EncryptionMethod?,
aesKeyStrength: AesKeyStrength?
): ZipParameters? {
val zipParameters = ZipParameters()
zipParameters.compressionMethod = compressionMethod
zipParameters.compressionLevel = compressionLevel
return zipParameters
}
fun zipFileAtPath(sourcePath: String?, toLocation: ParcelFileDescriptor?): Boolean {
println("zipFileAtPath is called")
val BUFFER = 2048
val sourceFile = File(sourcePath!!)
val zipParameters = buildZipParameters(CompressionMethod.DEFLATE, CompressionLevel.NORMAL,
false, null, null)
try {
var origin: BufferedInputStream? = null
val desc = toLocation
val dest = FileOutputStream(desc!!.fileDescriptor)
val out = ZipOutputStream(BufferedOutputStream(dest))
if (sourceFile.isDirectory) {
zipParameters.rootFolderNameInZip = sourcePath
zipSubFolder(out, sourceFile, sourceFile.parent!!.length, zipParameters!!)
} else {
val data = ByteArray(BUFFER)
val fi = FileInputStream(sourcePath)
origin = BufferedInputStream(fi, BUFFER)
zipParameters!!.fileNameInZip = getLastPathComponent(sourcePath)
zipParameters.lastModifiedFileTime = sourceFile.lastModified()
out.putNextEntry(zipParameters)
var count: Int = 0
while (fi.read(data).also({ count = it }) != -1) {
out.write(data, 0, count)
}
}
out.close()
} catch (e: java.lang.Exception) {
e.printStackTrace()
return false
}
return true
}
#Throws(IOException::class)
private fun zipSubFolder(
out: ZipOutputStream, folder: File, basePathLength: Int, zipParameters: ZipParameters
) {
val BUFFER = 2048
val fileList = folder.listFiles()
var origin: BufferedInputStream
fileList?.forEach { file ->
if (file.isDirectory) {
zipSubFolder(out, file, basePathLength, zipParameters)
} else {
val data = ByteArray(BUFFER)
val unmodifiedFilePath = file.path
val relativePath = unmodifiedFilePath
.substring(basePathLength)
val fi = FileInputStream(unmodifiedFilePath)
origin = BufferedInputStream(fi, BUFFER)
zipParameters.fileNameInZip = relativePath
zipParameters.lastModifiedFileTime = file.lastModified()
out.putNextEntry(zipParameters)
var count: Int = 0
while (fi.read(data).also({ count = it }) != -1) {
out.write(data, 0, count)
}
origin.close()
}
}
}
fun getLastPathComponent(filePath: String): String? {
val segments = filePath.split("/").toTypedArray()
return if (segments.size == 0) "" else segments[segments.size - 1]
}
}
I would appreciate it if someone could tell me what could be the problem.
I guess you forgot to closeEntry() after writing date for that particular entry. You need to do out.closeEntry() after the while loop of writing data. Have a look at the example here.
while (fi.read(data).also({ count = it }) != -1) {
out.write(data, 0, count)
}
out.closeEntry() // --> close entry after writing data
Related
I have an AudioTrack but still don't know how to change the pitch properly since the effect changes but the sound speed changes by simpleRateInHz too. Still don't know how to solve this properly
val file = File(dirPath, fileName)
val shortSizeInBytes = java.lang.Short.SIZE / java.lang.Byte.SIZE
val bufferSizeInBytes = (file.length() / shortSizeInBytes).toInt()
val audioData = ShortArray(bufferSizeInBytes)
try {
val inputStream = FileInputStream(file)
val bufferInputStream = BufferedInputStream(inputStream)
val dataFileInputStream = DataInputStream(bufferInputStream)
var j = 0
Log.d("debugging", "RUNNING")
while (dataFileInputStream.available() > 0) {
audioData[j] = dataFileInputStream.readShort()
j++
}
Log.d("debugging", "EXIT LOOP")
dataFileInputStream.close()
mAudioTrack = AudioTrack(3, 41000, 2, 2, bufferSizeInBytes, 1)
mAudioTrack?.play()
mAudioTrack?.write(audioData, 0, bufferSizeInBytes)
} catch (e: FileNotFoundException) {
e.printStackTrace()
} catch (e: IOException) {
e.printStackTrace()
}
I want to download a file into my library using URLConnection.
The size of the picture stored in the server is about 16mb, but the size of the saved picture is 84B.
There seems to be an error while reading the buffer, can you tell me what kind of error it is?
binding.signDownloadBtn.setOnClickListener {
val userId = binding.userIdText.text.toString()
val filetype = "user"
val fileSeparation = "sign"
Thread {
val spec = "myurl/${userId}/${filetype}/${fileSeparation}"
val outputDir = Environment.getExternalStorageDirectory().toString() + "/" + Environment.DIRECTORY_DOWNLOADS
var `is`: InputStream? = null
var os: FileOutputStream? = null
try {
val url = URL(spec)
val conn = url.openConnection() as HttpURLConnection
conn.requestMethod = "GET"
conn.setRequestProperty("Content-Type", "application/json;utf-8")
conn.setRequestProperty("Accept", "application/json")
conn.setRequestProperty("token", user_token)
conn.setRequestProperty("sysCd", sysCd)
conn.connectTimeout = 1500
println("responseCode ${conn.responseCode}")
if (conn.responseCode == HttpURLConnection.HTTP_OK) {
var fileName = ""
val disposition = conn.getHeaderField("Content-Disposition")
val contentType = conn.contentType
if (disposition != null) {
val target = "filename="
val index = disposition.indexOf(target)
if (index != -1) {
fileName =
disposition.substring(index + target.length)
fileName =
fileName.replace("\"", "")
}
println("Content-Type = $contentType")
println("Content-Disposition = $disposition")
println("fileName = $fileName")
`is` = conn.inputStream
os = FileOutputStream(File(outputDir, fileName))
val BUFFER_SIZE = 4096
var bytesRead: Int
val buffer = ByteArray(BUFFER_SIZE)
while (`is`.read(buffer).also { bytesRead = it } != -1) {
os.write(buffer, 0, bytesRead)
}
os.close()
`is`.close()
}
println("File downloaded")
} else {
println("No file to download. Server replied HTTP code: ${conn.responseCode}")
}
conn.disconnect()
} catch (e: Exception) {
println("An error occurred while trying to download a file.")
e.printStackTrace()
try {
`is`?.close()
os?.close()
} catch (e1: IOException) {
e1.printStackTrace()
}
}
}.start()
}
I try to simplify my java gzip uncompress code to kotlin. But after I changed, it sames broken.
Here is the java code
public static byte[] uncompress(byte[] compressedBytes) {
if (null == compressedBytes || compressedBytes.length == 0) {
return null;
}
ByteArrayOutputStream out = null;
ByteArrayInputStream in = null;
GZIPInputStream gzipInputStream = null;
try {
out = new ByteArrayOutputStream();
in = new ByteArrayInputStream(compressedBytes);
gzipInputStream = new GZIPInputStream(in);
byte[] buffer = new byte[256];
int n = 0;
while ((n = gzipInputStream.read(buffer)) >= 0) {
out.write(buffer, 0, n);
}
return out.toByteArray();
} catch (IOException ignore) {
} finally {
CloseableUtils.closeQuietly(gzipInputStream);
CloseableUtils.closeQuietly(in);
CloseableUtils.closeQuietly(out);
}
return null;
}
This is my kotlin code.
payload = GZIPInputStream(payload.inputStream())
.bufferedReader()
.use { it.readText() }
.toByteArray()
And I got this error.
com.google.protobuf.nano.InvalidProtocolBufferNanoException: While parsing a protocol message, the input ended unexpectedly in the middle of a field. This could mean either than the input has been truncated or that an embedded message misreported its own length.
It seems that the decompression process was interrupted by reader?
The readText(charset: Charset = Charsets.UTF_8) decodes the bytes into UTF-8 character set, which is why it says "This could mean either than the input has been truncated" it probably have tried to convert 8-bits into a Char and build a String out of it.
Use the readBytes() to get ByteArray which is represented same as byte[] in JVM platform.
Example:
GZIPInputStream(payload.inputStream())
.bufferedReader()
.use { it.readBytes() }
Edit:
For reading bytes, you shouldn't be using the Reader, it is meant for reading the Text in UTF-8 format as defined in the Kotlin's InputStream.bufferedReader:
public inline fun InputStream.bufferedReader(charset: Charset = Charsets.UTF_8): BufferedReader = reader(charset).buffered()
The InputStream.readBytes() will read the bytes at a buffer of 8KB itself.
public fun InputStream.readBytes(): ByteArray {
val buffer = ByteArrayOutputStream(maxOf(DEFAULT_BUFFER_SIZE, this.available()))
copyTo(buffer)
return buffer.toByteArray()
}
// This copies with 8KB buffer automatically
// DEFAULT_BUFFER_SIZE = 8 * 1024
public fun InputStream.copyTo(out: OutputStream, bufferSize: Int = DEFAULT_BUFFER_SIZE): Long {
var bytesCopied: Long = 0
val buffer = ByteArray(bufferSize)
var bytes = read(buffer)
while (bytes >= 0) {
out.write(buffer, 0, bytes)
bytesCopied += bytes
bytes = read(buffer)
}
return bytesCopied
}
So you just have to do:
GZIPInputStream(payload.inputStream()).use { it.readBytes() }
use the following function:
fun File.unzip(unzipLocationRoot: File? = null) {
val rootFolder = unzipLocationRoot
?: File(parentFile.absolutePath + File.separator + nameWithoutExtension)
if (!rootFolder.exists()) {
rootFolder.mkdirs()
}
ZipFile(this).use { zip ->
zip
.entries()
.asSequence()
.map {
val outputFile = File(rootFolder.absolutePath + File.separator + it.name)
ZipIO(it, outputFile)
}
.map {
it.output.parentFile?.run {
if (!exists()) mkdirs()
}
it
}
.filter { !it.entry.isDirectory }
.forEach { (entry, output) ->
zip.getInputStream(entry).use { input ->
output.outputStream().use { output ->
input.copyTo(output)
}
}
}
}
}
Pass the file as a parameter as follow:
val zipFile = File(your file directory, your file name)
zipFile.unzip()
Hope this would help 🙏🏼
I am unable to transmit an entire file using WiFi-Direct. The file sender is indicating that the entire file has been copied over to the socket output stream. The file receiver is only receiving roughly half of the file.
I looked at the contents of both the original file and the contents of the file storing the received data, and found the receiver is only receiving pieces of the original file. For example, it would receive bytes 0-100, and then it would jump to byte 245-350.
Why is the receiver only receiving bits and pieces of the file, rather than the entire file?
File Receiving Logic
private class FileReceiveThread(val channel: Channel) : TransmissionThread() {
private var mFileName: String = ""
private var mFileSize: Long = 0L
private var mBytesReceivedTotal = 0L
override fun run() {
try {
Timber.d("File receive thread running: fileSize=$mFileSize, fileName=$mFileName")
val outputFile = File.createTempFile("file", "")
val fileOutput = outputFile.outputStream()
val channelInput = channel.getInputStream().unwrap()
val inputBuffer = ByteArray(FILE_TX_BUFFER_SIZE)
var bytesReceived = channelInput.read(inputBuffer)
while (bytesReceived > 0) {
fileOutput.write(inputBuffer)
mBytesReceivedTotal += bytesReceived
Timber.d("Received $mBytesReceivedTotal total bytes")
bytesReceived = channelInput.read(inputBuffer)
}
onTransmitComplete?.invoke()
} catch (e: Exception) {
e.printStackTrace()
}
}
fun start(filename: String, size: Long) {
mFileName = filename
mFileSize = size
start()
}
}
File Sending Logic
private class FileSendThread : TransmissionThread() {
var mFile: File? = null
var mOutputStream: OutputStream? = null
override fun run() {
if (mFile != null && mOutputStream != null) {
val inputStream = mFile!!.inputStream()
val channelStream = mOutputStream!!
val buffer = ByteArray(FILE_TX_BUFFER_SIZE)
var bytesRead = inputStream.read(buffer)
var totalBytesRead = 0L + bytesRead
while (bytesRead > 0) {
Timber.v("Read $bytesRead, total $totalBytesRead")
channelStream.write(buffer)
bytesRead = inputStream.read(buffer)
totalBytesRead += bytesRead
}
Timber.d("Wrote file to output stream")
inputStream.close()
Timber.d("No more data to send")
onTransmitComplete?.invoke()
} else Timber.d("Parameters null: file=$mFile")
}
fun start(file: File, stream: OutputStream) {
mFile = file
mOutputStream = stream
start()
}
}
while (inputStream.read(buffer) > 0) {
channelStream.write(buffer)
}
The read() will often not fill the complete buffer. Hence if you write the buffer then only as far as it is filled.
var totalbytesread = 0;
var nread;
while ((nread = inputStream.read(buffer)) > 0) {
channelStream.write(buffer, 0, nread)
totalbytesread += nread;
}
channelStream.close()';
Log the totalbytesread.
Your original code would have caused a bigger received file so there is something else to be discovered..
I have a method which accept file and size of chunks and return list of chunked files. But the main problem that my line in file could be broken, for example in main file I have next lines:
|1|aaa|bbb|ccc|
|2|ggg|ddd|eee|
After split I could have in one file:
|1|aaa|bbb
In another file:
|ccc|2|
|ggg|ddd|eee|
Here is the code:
public static List<File> splitFile(File file, int sizeOfFileInMB) throws IOException {
int counter = 1;
List<File> files = new ArrayList<>();
int sizeOfChunk = 1024 * 1024 * sizeOfFileInMB;
byte[] buffer = new byte[sizeOfChunk];
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file))) {
String name = file.getName();
int tmp = 0;
while ((tmp = bis.read(buffer)) > 0) {
File newFile = new File(file.getParent(), name + "."
+ String.format("%03d", counter++));
try (FileOutputStream out = new FileOutputStream(newFile)) {
out.write(buffer, 0, tmp);
}
files.add(newFile);
}
}
return files;
}
Should I use RandomAccessFile class for above purposes (main file is really big - more then 5 Gb)?
If you don't mind to have chunks of different lengths (<=sizeOfChunk but closest to it) then here is the code:
public static List<File> splitFile(File file, int sizeOfFileInMB) throws IOException {
int counter = 1;
List<File> files = new ArrayList<File>();
int sizeOfChunk = 1024 * 1024 * sizeOfFileInMB;
String eof = System.lineSeparator();
try (BufferedReader br = new BufferedReader(new FileReader(file))) {
String name = file.getName();
String line = br.readLine();
while (line != null) {
File newFile = new File(file.getParent(), name + "."
+ String.format("%03d", counter++));
try (OutputStream out = new BufferedOutputStream(new FileOutputStream(newFile))) {
int fileSize = 0;
while (line != null) {
byte[] bytes = (line + eof).getBytes(Charset.defaultCharset());
if (fileSize + bytes.length > sizeOfChunk)
break;
out.write(bytes);
fileSize += bytes.length;
line = br.readLine();
}
}
files.add(newFile);
}
}
return files;
}
The only problem here is file charset which is default system charset in this example. If you want to be able to change it let me know. I'll add third parameter to "splitFile" function for it.
Just in case anyone is interested in a Kotlin version.
It creates an iterator of ByteArray chunks:
class ByteArrayReader(val input: InputStream, val chunkSize: Int, val bufferSize: Int = 1024*8): Iterator<ByteArray> {
var eof: Boolean = false
init {
if ((chunkSize % bufferSize) != 0) {
throw RuntimeException("ChunkSize(${chunkSize}) should be a multiple of bufferSize (${bufferSize})")
}
}
override fun hasNext(): Boolean = !eof
override fun next(): ByteArray {
var buffer = ByteArray(bufferSize)
var chunkWriter = ByteArrayOutputStream(chunkSize) // no need to close - implementation is empty
var bytesRead = 0
var offset = 0
while (input.read(buffer).also { bytesRead = it } > 0) {
if (chunkWriter.use { out ->
out.write(buffer, 0, bytesRead)
out.flush()
offset += bytesRead
offset == chunkSize
}) {
return chunkWriter.toByteArray()
}
}
eof = true
return chunkWriter.toByteArray()
}
}
Split a file to multiple chunks (in memory operation), here I'm splitting any file to a size of 500kb(500000 bytes) and adding to a list :
public static List<ByteArrayOutputStream> splitFile(File f) {
List<ByteArrayOutputStream> datalist = new ArrayList<>();
try {
int sizeOfFiles = 500000;
byte[] buffer = new byte[sizeOfFiles];
try (FileInputStream fis = new FileInputStream(f); BufferedInputStream bis = new BufferedInputStream(fis)) {
int bytesAmount = 0;
while ((bytesAmount = bis.read(buffer)) > 0) {
try (OutputStream out = new ByteArrayOutputStream()) {
out.write(buffer, 0, bytesAmount);
out.flush();
datalist.add((ByteArrayOutputStream) out);
}
}
}
} catch (Exception e) {
//get the error
}
return datalist;
}
Split files in chunks depending upon your chunk size
val f = FileInputStream(file)
val data = ByteArray(f.available()) // Size of original file
var subData: ByteArray
f.read(data)
var start = 0
var end = CHUNK_SIZE
val max = data.size
if (max > 0) {
while (end < max) {
subData = data.copyOfRange(start, end)
start = end
end += CHUNK_SIZE
if (end >= max) {
end = max
}
//Function to upload your chunk
uploadFileInChunk(subData, isLast = false)
}
// For the Last Chunk
end--
subData = data.copyOfRange(start, end)
uploadFileInChunk(subData, isLast = true)
}
If you are taking the file from the user through intent you may get file URI as content, so in that case.
Uri uri = data.getData();
InputStream inputStream = getContext().getContentResolver().openInputStream(uri);
fileInBytes = IOUtils.toByteArray(inputStream);
Add the dependency in you build gradle to use IOUtils
compile 'commons-io:commons-io:2.11.0'
Now do a little modification in the above code to send your file to server.
var subData: ByteArray
var start = 0
var end = CHUNK_SIZE
val max = fileInBytes.size
if (max > 0) {
while (end < max) {
subData = fileInBytes.copyOfRange(start, end)
start = end
end += CHUNK_SIZE
if (end >= max) {
end = max
}
uploadFileInChunk(subData, isLast = false)
}
// For the Last Chunk
end--
subData = fileInBytes.copyOfRange(start, end)
uploadFileInChunk(subData, isLast = true)
}