omicronapps / 7-Zip-JBinding-4Android

Android Java wrapper for 7z archiver engine
GNU Lesser General Public License v2.1
121 stars 24 forks source link

Wrong directory structure on compression #22

Closed asthagarg2428 closed 2 years ago

asthagarg2428 commented 2 years ago

Suppose the file which I want to compress with name compressed_file.zip is at path -> data/0/abc/1.docx On extracting the compressed zip, I find that the file structure is same as that of 1.docx which is wrong.

Expected : compressed_file.zip/1.docx Actual : compressed_file.zip/data/0/abc/1.docx

Using this lib in Android project

Kitsune2222 commented 2 years ago

You can set any path for file in archive. http://sevenzipjbind.sourceforge.net/compression_snippets.html#create-specific-api-zip item.setPropertyPath("/1.docx")

asthagarg2428 commented 2 years ago

Thank you @Kitsune2222 , it worked :)))))

asthagarg2428 commented 2 years ago

@Kitsune2222 one more thing, since you are already using this library in your project, Does it support extracting/compression big files like greater than 4GB? How has been your overall experience with this lib?

Kitsune2222 commented 2 years ago

Don't know, did not check, but think, yes. I'm more not use this lib, because this have several bugs, not support some formats, no updates, and doesn't has SAF support. I'm use my own library based on https://github.com/jinfeihan57/p7zip I'll post my version for Android in a few months. Some improvements also needed.

asthagarg2428 commented 2 years ago

@Kitsune2222 what does it mean when you say 'doesn't has SAF support.'??

Also, regarding the other lib which uses p7zip, do you know command to check if any file is password protected before extracting the zip/7zip file? I also used one similar lib but got stuck.

Can you help me with your email id? I will be needing some help regarding p7zip thing.

Kitsune2222 commented 2 years ago

SAF - Storage Access Framework. All new version android use Uri for file access, this library can't work on microSD or files with Uri. This lib support only legacy java File class. Password request automatically when open or extract archive in callback. Manually check, at least not in this library. http://sevenzipjbind.sourceforge.net/compression_snippets.html help here. With p7zip i'm can't help, it's harder there, i'm only later can post my library based on new p7zip. Now you can use this lib, if it suits you.

Kitsune2222 commented 2 years ago

If you need only zip https://github.com/srikanth-lingala/zip4j is good lib

asthagarg2428 commented 2 years ago

zip4j I already use of zip, now I need to support 7zip. for password I was asking about p7zip approach, where we pass command to execute. This is my complete doubt - https://stackoverflow.com/questions/71558436/command-to-extract-password-protected-zip-file-using-p7zip

Kitsune2222 commented 2 years ago

This lib more functional, than AndroidP7Zip, because password request automatically when it need. Has progress. I'm think you can use it. SAF support you also can add if it need. I think this lib it's a good choice too.

asthagarg2428 commented 2 years ago

Yes, I couldn't find any command through which I can support password protected extraction using wrappers over p7zip. Finding commands is really tough there

asthagarg2428 commented 2 years ago

@Kitsune2222 Is this library built for all the Android CPU Architectures like arm, arm64, x86 etc ?? I couldn't find anything in the make file related to this

Kitsune2222 commented 2 years ago

arm, arm64, x86, x86_64, you can see compiled files in apk for all 4 cpu arch.

omicronapps commented 2 years ago

Yes, 7-Zip-JBinding-4Android is a direct port of the Java library 7-Zip-JBinding. This does mean that it is limited to the functionality included in the original Java library, with no Android specific functionality or other features added. Also any updates are dependent on updates to the original 7-Zip-JBinding library. On the other hand, it does come with a rich feature set and support for many archive formats, and I have found to be useful for quick extraction of large archives. However, depending on your project it may or may not be a good fit, so agree it would be a good idea to compare with other library implementations.

asthagarg2428 commented 2 years ago

@Kitsune2222 @omicronapps I'm unable to extract a bzip2, gzip file, however same code is working fine for zip and 7z files. I tried passing null,BZIP2, gzip as ArchiveFormat.

https://github.com/omicronapps/7-Zip-JBinding-4Android/issues/25

Kitsune2222 commented 2 years ago

Gzip can contains only one file (tar.gz archive, contains tar archive, which contains files), you need extract gzip, you get only one tar archive, and then extract tar. Bzip2, etc, the same.

asthagarg2428 commented 2 years ago

@Kitsune2222 Thanks for your quick reply On trying to extract a gzip file (text.tar.gz), what I understood from your reply is that I need to open the gzip file, then extract it then I will obtain the tar file and then extract tar files' child.

So I have a doubt, on extracting the tar out of gzip, which Property of IInArchive should I use ? Following are the code changes:-

inArchive = SevenZip.openInArchive(null, inStream);   // this is for gzip file
            int itemCount = inArchive.getNumberOfItems();

            OutputStream out = null;
            try {
                out = FileUtils.openOutputStream(?????);
                SequentialOutStream outStream = new SequentialOutStream(out);
                ExtractOperationResult result = inArchive.extractSlow(0, outStream);  // How to fetch the tar out of gzip?

                if (result != ExtractOperationResult.OK) {
                    Log.e(TAG, "ExtractOperationResult :: " +result.toString());
                }else
                    Log.d(TAG, "Extract Exit");
            } catch (Exception e) {
                e.printStackTrace();
            }
Kitsune2222 commented 2 years ago

Looks good, also you can use inArchive.extract() There may be problems with some types of archives, if you get error. I have come across some of them. Try this test archive: https://drive.google.com/file/d/1KGpiVme3KEqSZ_dQTZF0WBn2nkjH68HE/view?usp=sharing

asthagarg2428 commented 2 years ago

@Kitsune2222 on extracting the gzip file to get the tar, which Property ID of IInArchive should I use ??? (In the above code snippet, I have added ??? at 6th line) Where(using which Prop.ID) can I get the data related to the tar which is there inside gzip?

Currently for zip/7zip/tar, I use the PropID.PATH to create File object, but in case of gzip/bzip2 the value of inArchive.getStringProperty(i, PropID.PATH) is empty.

Kitsune2222 commented 2 years ago

You can use your own file path, because only one file can be in gzip, id always is 0, Gzip doesn't contain metadata, it's not an archive, it's compression method. So use your file path where you want to get the tar archive. FileUtils.openOutputStream("/sdcard/test.tar") Then you can extract /sdcard/test.tar and delete it. If you want see tar.gz content, you need fully unpack tar.gzip to tar, and then you can see tar content. No other way.

asthagarg2428 commented 2 years ago

@Kitsune2222 Thanks :)

asthagarg2428 commented 2 years ago

@Kitsune2222 @omicronapps Just another doubt, I want to differentiate among the following cases, based on which I want the UI layer to make corresponding action.

  1. For a password protected file, passed password is empty (Action: I want user to be prompted with a dialog to 'enter password')
  2. For a password protected 7zip file, the passed password is incorrect. (Action: I want to show error message to 'enter correct password')
  3. Suppose I pass .aac file which is not supported (I want to show error message)

Currently what I'm observing is that in all the above cases I'm getting SevenZipException with message as File can't be opened with any of the registered codecs. Also this behaviour is different than that of zip(for zip is clear and straight forward). I'm thinking to return 3 different error code enums but I'm unable to differentiate among above error cases.

I'm using below code:

inArchive = SevenZip.openInArchive(
                null, inStream, OpenInCallback(password, logger.get())
            )

Thanks in advance :)

borisbrodski commented 2 years ago

Passwords: 7z differentiate between password protected items (file content) and password protected list of items (file and folder names). Passing a password while opening 7z archive only applies to the password protected list of items, not for password protected archive items.

Error messages: SevenZipJBinding extracts content, not files. The method "openInArchive" opens a byte stream, never getting the name of the file. Considering only file content and not file extension, we can only either manage to file a correct archive method and open the archive or not manage to find one. There is currently no way to generate correct error message. Sorry

asthagarg2428 commented 2 years ago

Passwords: 7z differentiate between password protected items (file content) and password protected list of items (file and folder names). Passing a password while opening 7z archive only applies to the password protected list of items, not for password protected archive items.

Error messages: SevenZipJBinding extracts content, not files. The method "openInArchive" opens a byte stream, never getting the name of the file. Considering only file content and not file extension, we can only either manage to file a correct archive method and open the archive or not manage to find one. There is currently no way to generate correct error message. Sorry

You mean I cannot differentiate if entered password is incorrect OR user has not entered the password OR the passed file format is not supported?

Kitsune2222 commented 2 years ago
  1. Check empty edittext field.
  2. This lib doesn't support this. Any case throws SevenZipException.
  3. Check file extension. Maybe magic number format in file header. Before extract.
asthagarg2428 commented 2 years ago
  • Check empty edittext field.
  • This lib doesn't support this. Any case throws SevenZipException.
  • Check file extension, maybe magic number in file header, in your app, before extract.

@Kitsune2222 regarding "1. Check empty edittext field.", in order to show the dialog with edit text itself I need to get the callback/error code saying that the file is protected.

But I feel I can not differentiate among the above 3 cases :(

omicronapps commented 2 years ago

You can determine if an archive item is password protected after calling IInArchive.extract(), by checking inArchive.getProperty(index, PropID.ENCRYPTED) in IArchiveExtractCallback.getStream(). If this is 'true' then [ICryptoGetTextPassword.cryptoGetTextPassword()](http://sevenzipjbind.sourceforge.net/javadoc/net/sf/sevenzipjbinding/ICryptoGetTextPassword.html#cryptoGetTextPassword()) you should return the provide password.

Next if IArchiveExtractCallback.setOperationResult returns WRONG_PASSWORD it indicates that the password used was incorrect.

To determine if the file is a valid archive, SevenZip.openInArchive will throw SevenZipException with message "Archive file can't be opened with any of the registered codecs" if trying to open an invalid archive.

asthagarg2428 commented 2 years ago

Hi @omicronapps ,

Whatever you are suggesting is correct for zip but not for 7zip. In case of 7zip,

SevenZip.openInArchive(
                null, inStream, ArchiveOpenInCallback(password)

this itself crashes with SevenZipException and openInArchive is the foremost step to get inArchive object (Code won't reach to inArchive.getProperty(index, PropID.ENCRYPTED) call either)

omicronapps commented 2 years ago

I tested extracting a password encrypted 7zip archive, and it worked fine. Do you have example source code and 7zip archive to reproduce the issue?

Kitsune2222 commented 2 years ago

asthagarg2428 With this library you can't check is encrypted archive header or no. You need open UI dialog for password request in [cryptoGetTextPassword()](http://sevenzipjbind.sourceforge.net/javadoc/net/sf/sevenzipjbinding/ICryptoGetTextPassword.html#cryptoGetTextPassword()), then waiting user answer and return password. If wrong password, you get SevenZipException. Then retry open archive request.

asthagarg2428 commented 2 years ago

@Kitsune2222 in case of wrong password ofcourse I check for SevenZipException received but my query was SevenZipException is also received when the archive format is not supported. Unable to differentiate between these cases

Kitsune2222 commented 2 years ago

Before open check 7z extension and first bytes of file for 7z always: 0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C If other sequence it's not 7z archive. UPD. This correct .

byteArrayOf(0x50, 0x4B, 0x03, 0x04), // ZIP byteArrayOf(0x50, 0x4B, 0x05, 0x06), // ZIP (Empty) byteArrayOf(0x50, 0x4B, 0x07, 0x08), // ZIP (Split) byteArrayOf(0x37, 0x7A, 0xBC.toByte(), 0xAF.toByte(), 0x27, 0x1C), // 7z byteArrayOf(0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x01, 0x00), // RAR5 byteArrayOf(0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x00) // RAR4

asthagarg2428 commented 2 years ago

@Kitsune2222 Hey, I have one new issue now, which is related to different language. I have a 你好.docx file name under 你好.docx.zip file, on extracting it I see that the file name is garbled. I used macOS default compression to create that zip file. Any idea?

I saw somewhere that if code_page is not set while compressing that file, the text will be garbled. But the compression in not in our hand as of now, user is uploading that file. How can I handle this situation? I'm stuck.

cc: @omicronapps

omicronapps commented 2 years ago

See response in #15.

asthagarg2428 commented 2 years ago

@omicronapps Posted further query on #15