Saya menerima string dari proses eksternal. Saya ingin menggunakan String itu untuk membuat nama file, dan kemudian menulis ke file itu. Berikut potongan kode saya untuk melakukan ini:
String s = ... // comes from external source
File currentFile = new File(System.getProperty("user.home"), s);
PrintWriter currentWriter = new PrintWriter(currentFile);
Jika s berisi karakter yang tidak valid, seperti '/' dalam OS berbasis Unix, maka java.io.FileNotFoundException (dengan benar) dilemparkan.
Bagaimana saya dapat menyandikan String dengan aman sehingga dapat digunakan sebagai nama file?
Sunting: Yang saya harapkan adalah panggilan API yang melakukan ini untuk saya.
Aku bisa melakukan ini:
String s = ... // comes from external source
File currentFile = new File(System.getProperty("user.home"), URLEncoder.encode(s, "UTF-8"));
PrintWriter currentWriter = new PrintWriter(currentFile);
Tapi saya tidak yakin apakah URLEncoder itu dapat diandalkan untuk tujuan ini.
Jawaban:
Jika Anda ingin hasilnya menyerupai file asli, SHA-1 atau skema hashing lainnya bukanlah jawabannya. Jika tabrakan harus dihindari, maka penggantian atau penghapusan karakter "buruk" juga bukanlah jawabannya.
Sebaliknya Anda menginginkan sesuatu seperti ini. (Catatan: ini harus diperlakukan sebagai contoh ilustrasi, bukan sesuatu untuk disalin dan ditempel.)
Solusi ini memberikan pengkodean yang dapat dibalik (tanpa benturan) di mana string yang disandikan menyerupai string asli dalam banyak kasus. Saya berasumsi bahwa Anda menggunakan karakter 8-bit.
URLEncoder
berfungsi, tetapi memiliki kelemahan karena ia mengkodekan banyak karakter nama file legal.Jika Anda menginginkan solusi yang tidak dijamin menjadi dapat dibalik, cukup hapus karakter 'buruk' daripada menggantinya dengan urutan pelolosan.
Kebalikan dari pengkodean di atas harus sama-sama lurus ke depan untuk diterapkan.
sumber
Saran saya adalah untuk mengambil pendekatan "daftar putih", artinya jangan mencoba dan menyaring karakter buruk. Sebaliknya tentukan apa yang OK. Anda dapat menolak nama file atau memfilternya. Jika Anda ingin memfilternya:
Apa yang dilakukannya adalah mengganti karakter apa pun yang bukan angka, huruf, atau garis bawah dengan apa pun. Atau Anda dapat menggantinya dengan karakter lain (seperti garis bawah).
Masalahnya adalah jika ini adalah direktori bersama maka Anda tidak ingin nama file bertabrakan. Bahkan jika area penyimpanan pengguna dipisahkan oleh pengguna, Anda mungkin berakhir dengan nama file yang bertabrakan hanya dengan menyaring karakter buruk. Nama yang dimasukkan pengguna sering kali berguna jika mereka ingin mengunduhnya juga.
Untuk alasan ini saya cenderung mengizinkan pengguna untuk memasukkan apa yang mereka inginkan, menyimpan nama file berdasarkan skema yang saya pilih sendiri (misalnya userId_fileId) dan kemudian menyimpan nama file pengguna dalam tabel database. Dengan begitu, Anda dapat menampilkannya kembali kepada pengguna, menyimpan hal-hal yang Anda inginkan dan tidak membahayakan keamanan atau menghapus file lain.
Anda juga dapat mencirikan file (mis. Hash MD5) tetapi kemudian Anda tidak dapat mencantumkan file yang dimasukkan pengguna (toh tidak dengan nama yang berarti).
EDIT: Memperbaiki regex untuk java
sumber
"\\W+"
regexp di Java. Garis miring terbalik pertama kali diterapkan ke string itu sendiri, dan\W
bukan merupakan urutan escape yang valid. Saya mencoba mengedit jawabannya, tetapi sepertinya seseorang menolak suntingan saya :(Itu tergantung pada apakah pengkodean harus dibalik atau tidak.
Dapat dibalik
Gunakan pengkodean URL (
java.net.URLEncoder
) untuk mengganti karakter khusus dengan%xx
. Perhatikan bahwa Anda menangani kasus khusus di mana string sama.
, sama..
atau kosong! ¹ Banyak program menggunakan pengkodean URL untuk membuat nama file, jadi ini adalah teknik standar yang dipahami semua orang.Tidak dapat diubah
Gunakan hash (misalnya SHA-1) dari string yang diberikan. Algoritme hash modern ( bukan MD5) dapat dianggap bebas benturan. Faktanya, Anda akan mengalami terobosan dalam kriptografi jika Anda menemukan tabrakan.
¹ Anda dapat menangani semua 3 kasus khusus dengan elegan menggunakan awalan seperti
"myApp-"
. Jika Anda memasukkan file secara langsung ke dalamnya$HOME
, Anda harus melakukannya untuk menghindari konflik dengan file yang sudah ada seperti ".bashrc".sumber
Inilah yang saya gunakan:
Apa yang dilakukannya adalah mengganti setiap karakter yang bukan huruf, angka, garis bawah atau titik dengan garis bawah, menggunakan regex.
Ini berarti bahwa sesuatu seperti "Bagaimana mengubah £ menjadi $" akan menjadi "How_to_convert___to__". Memang, hasil ini tidak terlalu ramah pengguna, tetapi aman dan nama direktori / file yang dihasilkan dijamin berfungsi di mana-mana. Dalam kasus saya, hasilnya tidak ditampilkan kepada pengguna, dan karenanya tidak menjadi masalah, tetapi Anda mungkin ingin mengubah regex menjadi lebih permisif.
Perlu dicatat bahwa masalah lain yang saya temui adalah terkadang saya mendapatkan nama yang identik (karena ini didasarkan pada input pengguna), jadi Anda harus menyadarinya, karena Anda tidak dapat memiliki banyak direktori / file dengan nama yang sama dalam satu direktori. . Saya baru saja menambahkan waktu dan tanggal saat ini, dan string acak pendek untuk menghindarinya. (string acak aktual, bukan hash nama file, karena nama file yang identik akan menghasilkan hash yang identik)
Juga, Anda mungkin perlu memotong atau memperpendek string yang dihasilkan, karena mungkin melebihi batas 255 karakter yang dimiliki beberapa sistem.
sumber
Bagi mereka yang mencari solusi umum, ini mungkin kriteria umum:
Untuk mencapai ini, kita dapat menggunakan regex untuk mencocokkan karakter ilegal, mengenkodenya dalam persen , lalu membatasi panjang string yang dikodekan.
Pola
Pola di atas didasarkan pada subset konservatif dari karakter yang diperbolehkan dalam spesifikasi POSIX .
Jika Anda ingin mengizinkan karakter titik, gunakan:
Berhati-hatilah dengan string seperti "." dan ".."
Jika Anda ingin menghindari tabrakan pada sistem file yang tidak peka huruf besar kecil, Anda harus keluar dari kapital:
Atau hindari huruf kecil:
Daripada menggunakan daftar putih, Anda dapat memilih untuk memasukkan karakter yang dicadangkan ke daftar hitam untuk sistem file spesifik Anda. EG Regex ini sesuai dengan sistem file FAT32:
Panjangnya
Di Android, 127 karakter adalah batas aman. Banyak sistem file mengizinkan 255 karakter.
Jika Anda lebih suka mempertahankan ekor, daripada kepala senar, gunakan:
Decoding
Untuk mengubah nama file kembali ke string asli, gunakan:
Batasan
Karena string yang lebih panjang dipotong, ada kemungkinan nama bertabrakan saat encoding, atau rusak saat decoding.
sumber
Pattern.compile("[^A-Za-z0-9_\\-]")
Coba gunakan regex berikut yang menggantikan setiap karakter nama file yang tidak valid dengan spasi:
sumber
_
atau-
.Pilih racun Anda dari opsi yang disajikan oleh commons-codec , contoh:
sumber
sha1
;sha
sudah ditinggalkan.Ini mungkin bukan cara yang paling efektif, tetapi menunjukkan cara melakukannya menggunakan pipeline Java 8:
Solusinya dapat ditingkatkan dengan membuat kolektor kustom yang menggunakan StringBuilder, jadi Anda tidak perlu mentransmisikan setiap karakter ringan ke string kelas berat.
sumber
Anda dapat menghapus karakter yang tidak valid ('/', '\', '?', '*') Dan kemudian menggunakannya.
sumber