мне нужно выполнить задачу, как показано ниже: -
1). Загрузка файла в основное место: -
Я хочу читать из файла и записывать его в основное место (удаленный файловый сервер).
2). Загрузка файла в несколько дополнительных мест: -
В то же время, когда идет запись в основное место, параллельно Я хочу прочитать несколько фрагментов байтов из файла основного местоположения и записать его в несколько вторичных местоположений.
Я пробовал приведенную ниже программу для вышеуказанного подхода: -
BufferedInputStream bin = null;
ReadableByteChannel channel = null;
int bufferSize = 1048576;
int readBufferSize = 1024*4;
java.nio.ByteBuffer byteBuffer = java.nio.ByteBuffer.allocate(readBufferSize);
InputStream is = new FileInputStream(new File("D:\\Harisingh\\300MB.txt"));
bin = new BufferedInputStream(is,bufferSize);
channel = Channels.newChannel(bin);
int retryCnt = 0;
ByteArrayOutputStream baOS = new ByteArrayOutputStream(bufferSize);
int totalBytes=0;
int itrCount=0;
int maxIterateCnt = 1;
int len;
//primary location writing
SmbFile smbFile = new SmbFile("smb://user:[email protected]/data/Harisingh/collab_4_1_4/primary.txt");
BufferedOutputStream bFout = new BufferedOutputStream(new SmbFileOutputStream(smbFile));
SmbFileInputStream fis = new SmbFileInputStream("smb://user:[email protected]/data/Harisingh/collab_4_1_4/primary.txt");
BufferedInputStream binPrimary = new BufferedInputStream(fis);
SmbFileOutputStream secLocation1= new SmbFileOutputStream(new SmbFile("smb://user:[email protected]/data/Harisingh/collab_4_1_4/Secondary1.txt"));
SmbFileOutputStream secLocation2 = new SmbFileOutputStream(new SmbFile("smb://user:[email protected]/data/Harisingh/collab_4_1_4/Secondary2.txt"));
SmbFileOutputStream secLocation3 = new SmbFileOutputStream(new SmbFile("smb://user:[email protected]/data/Harisingh/Secondary/Secondary3.txt"));
try {
if (bufferSize > readBufferSize){
maxIterateCnt = bufferSize/readBufferSize;
}
while((len=channel.read(byteBuffer))>=0)
{
itrCount++;
totalBytes+=len;
baOS.write(byteBuffer.array(),0,len);
if (itrCount>=maxIterateCnt)
{
//primary location writing
try{
bFout.write(baOS.toByteArray(),0,totalBytes);
}catch(Exception se)
{
}
// secondary location writing
new Thread(){
public void run(){
System.out.println("Thread Running");
try {
int count;
byte[] readByteArray = new byte[1024*4];
while ((count = binPrimary.read(readByteArray)) != -1)
{
secLocation1.write(readByteArray, 0, count);
secLocation2.write(readByteArray, 0, count);
secLocation3.write(readByteArray, 0, count);
readByteArray = new byte[1024*4];
count= 0;
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}.start();
totalBytes=0;
baOS.reset();
itrCount=0;
}
byteBuffer.clear();
}
//primary location writing
try{
bFout.write(baOS.toByteArray(),0,totalBytes);
}catch(Exception se)
{
}
bFout.flush();
bFout.close();
int count;
// secondary location writing
new Thread(){
public void run(){
System.out.println("Thread Running");
try {
int count;
byte[] readByteArray = new byte[1024*4];
while ((count = binPrimary.read(readByteArray)) != -1)
{
secLocation1.write(readByteArray, 0, count);
secLocation2.write(readByteArray, 0, count);
secLocation3.write(readByteArray, 0, count);
readByteArray = new byte[1024*4];
count= 0;
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}.start();
Теперь с помощью вышеуказанной программы он записывает файл в первичное расположение основным потоком, а запись вторичного местоположения выполняется в отдельном потоке, но я сталкиваюсь с проблемой записи некоторых байтов, отсутствующих в некоторых вторичных местах из-за многопоточности.
К вашему сведению
Этот вопрос относится только к потоку io. Это не относится к JCIFS, поэтому вы можете использовать ту же программу с простым потоком io, не требуя потока smb io. Не могли бы вы помочь мне разобраться в этом?
@VitalyZ Спасибо за быстрый ответ. Теперь он отлично работает с переносом SmbFileOutputStream в BufferedOutputStream.
@VitalyZ В соответствии с предыдущим комментарием, он работал нормально, заключая SmbFileOutputStream в BufferedOutputStream и промывая, но теперь стало известно, что размер файла кажется одинаковым для всех вторичных местоположений, но содержимое файла отличается и его не соответствует. Я сравнил MD5 вторичных файлов, но для всех он выглядит по-разному. Не могли бы вы помочь мне разобраться, почему размер файла такой же, но содержимое файла отличается. Кажется, что все еще что-то не так с записью файла.
Каждый раз, когда вы пишете в первичный, вы создаете поток, который будет записывать все вторичные файлы заново с самого начала? Я думаю, вам следует отслеживать, сколько байтов было записано в первичный, сколько вы прочитали из первичного в вторичный и поиграться с чтением смещения. Также я не знаю, есть ли реальная выгода от вторичной записи в параллельных потоках, только сбивает с толку обработку. 1. Чтение файла, 2. WritePR, 3. Сброс, 4. ReadPR, 5. WriteSEC, 6. WriteSEC, 7. WriteSEC, 8. Очистка, 9. Повторить.
@Jokkeri На самом деле у меня нет контроля над потоком для чтения байтов, последовательно записанных в основной файл. Есть ли у вас идеи, как я могу отслеживать его и последовательно записывать во вторичный файл, чтобы содержимое файла было правильным? Или что вы предлагаете? Следует ли мне читать основной файл после того, как он полностью записан? или я могу прочитать это, пока идет запись?
@HarisinghRajput Зависит от основной цели. Просто скопировать файлы или скопировать файлы наиболее эффективным способом ... Самый простой способ - сначала полностью скопировать первичный файл, а затем скопировать его во вторичные места. Вопрос; в чем причина "основного" местоположения? Не могли бы вы просто скопировать исходный файл в 4 одинаковых места?
@Jokkeri Да, теперь мы пытаемся использовать самый простой способ - сначала полностью скопировать первичный файл, а затем скопировать его во вторичные места, что является прямым способом. И он работает нормально. Не могли бы вы просто скопировать исходный файл в 4 одинаковых места? Если мы пойдем с этим подходом, то это ухудшит нашу производительность, потому что мы должны дождаться завершения записи 4 местоположений. Наша цель первичного местоположения состоит в том, что сначала мы должны дождаться загрузки только первичного местоположения, а запись вторичных местоположений должна выполняться асинхронно в фоновом режиме, поэтому мы передаем этот процесс потокам.
@Jokkeri Как только загрузка файла первичного местоположения завершится, мы дадим ответ об успешном завершении, независимо от того, завершена или ожидает загрузка файла вторичного местоположения. Думаю, теперь вам понятнее
Вот пример, который я не рекомендую использовать «как есть» - его намерение состоит в том, чтобы действовать как доказательство концепции. В примере сначала выполняется первичный процесс, чтобы достичь максимальной производительности на этом этапе. Затем вторичные операции выполняются каждый в собственном параллельном потоке.
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.UnknownHostException;
import jcifs.smb.NtlmPasswordAuthentication;
import jcifs.smb.SmbException;
import jcifs.smb.SmbFile;
import jcifs.smb.SmbFileInputStream;
import jcifs.smb.SmbFileOutputStream;
public class testSmb {
static boolean append = true;
static int threadCount = 0;
static int bufferSize = 2048;
static NtlmPasswordAuthentication auth;
static File localFile;
static SmbFile primarySmbFile;
static BufferedInputStream input;
static SmbFileOutputStream output;
static SmbFile secondary1SmbFile;
static BufferedInputStream sec1Input;
static SmbFileOutputStream sec1Output;
static SmbFile secondary2SmbFile;
static BufferedInputStream sec2Input;
static SmbFileOutputStream sec2Output;
static SmbFile secondary3SmbFile;
static BufferedInputStream sec3Input;
static SmbFileOutputStream sec3Output;
public static Object lock = new Object();
public static void main(String... args) throws IOException {
System.out.println("Main thread Started");
init();
write(input, output);
writeInThread(sec1Input, sec1Output);
writeInThread(sec2Input, sec2Output);
writeInThread(sec3Input, sec3Output);
System.out.println("Main thread Finished");
}
public static void init() throws MalformedURLException,
FileNotFoundException, SmbException, UnknownHostException {
localFile = new File("c:\\temp\\myFile.txt");
if (localFile.length() > 20971520l) {
bufferSize = 131072;
}
String server = "myServer";
String username = "myUser";
String password = "myPass";
String path = "myPath";
auth = new NtlmPasswordAuthentication(server, username, password);
input = new BufferedInputStream(new FileInputStream(localFile));
primarySmbFile = new SmbFile("smb://" + server + "/" + path
+ "/primary.txt", auth, SmbFile.FILE_SHARE_READ
| SmbFile.FILE_SHARE_WRITE | SmbFile.FILE_SHARE_DELETE);
output = new SmbFileOutputStream(primarySmbFile, append);
if (!primarySmbFile.exists()) {
primarySmbFile.createNewFile();
}
sec1Input = new BufferedInputStream(new SmbFileInputStream(new SmbFile(
primarySmbFile, primarySmbFile.getName())));
secondary1SmbFile = new SmbFile("smb://" + server + "/" + path
+ "/secondary1.txt", auth, SmbFile.FILE_SHARE_READ
| SmbFile.FILE_SHARE_WRITE | SmbFile.FILE_SHARE_DELETE);
sec1Output = new SmbFileOutputStream(secondary1SmbFile, append);
if (!secondary1SmbFile.exists()) {
secondary1SmbFile.createNewFile();
}
sec2Input = new BufferedInputStream(new SmbFileInputStream(new SmbFile(
primarySmbFile, primarySmbFile.getName())));
secondary2SmbFile = new SmbFile("smb://" + server + "/" + path
+ "/secondary2.txt", auth, SmbFile.FILE_SHARE_READ
| SmbFile.FILE_SHARE_WRITE | SmbFile.FILE_SHARE_DELETE);
sec2Output = new SmbFileOutputStream(secondary2SmbFile, append);
if (!secondary2SmbFile.exists()) {
secondary2SmbFile.createNewFile();
}
sec3Input = new BufferedInputStream(new SmbFileInputStream(new SmbFile(
primarySmbFile, primarySmbFile.getName())));
secondary3SmbFile = new SmbFile("smb://" + server + "/" + path
+ "/secondary3.txt", auth, SmbFile.FILE_SHARE_READ
| SmbFile.FILE_SHARE_WRITE | SmbFile.FILE_SHARE_DELETE);
sec3Output = new SmbFileOutputStream(secondary3SmbFile, append);
if (!secondary3SmbFile.exists()) {
secondary3SmbFile.createNewFile();
}
}
public static void write(BufferedInputStream bufferedInputStream,
SmbFileOutputStream smbFileOutputStream) throws IOException {
byte[] buffer = new byte[bufferSize];
int len = 0;
try {
while ((len = bufferedInputStream.read(buffer)) > 0) {
synchronized (lock) {
System.out.println("'" + Thread.currentThread().getName()
+ "' writing " + bufferSize + "bytes");
smbFileOutputStream.write(buffer, 0, len);
smbFileOutputStream.flush();
}
}
} catch (IOException e) {
throw e;
} finally {
try {
bufferedInputStream.close();
} catch (Exception e) {
}
try {
smbFileOutputStream.flush();
smbFileOutputStream.close();
} catch (Exception e) {
}
}
}
public static void writeInThread(
final BufferedInputStream bufferedInputStream,
final SmbFileOutputStream smbFileOutputStream) {
threadCount++;
new Thread("Secondary thread " + threadCount) {
public void run() {
System.out.println(Thread.currentThread().getName()
+ ": started");
try {
write(bufferedInputStream, smbFileOutputStream);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ ": finished");
}
}.start();
}
}
Спасибо, что поделились программой. Он работает нормально, поскольку мы обсуждали, что он начинает загружать файл во второстепенные места после завершения загрузки основного файла. Теперь у меня вопрос, что все второстепенные локации записываются параллельно или одно за другим последовательно?
Большое спасибо за то, что поделились программой. Мы сделали то же самое, что я прокомментировал ранее. мы пытаемся самым простым способом - сначала полностью скопировать первичный файл, а затем скопировать его во вторичные места, что является прямым способом У нас нет проблем с этим подходом. Мы столкнулись с проблемой только при одновременном чтении и записи в основной файл.
Отредактировал пример, чтобы сделать его параллельным
jcifs.smb.SmbException: недействительный дескриптор.
@HarisinghRajput Как насчет этого (отредактировал пример), добавил некоторую синхронизацию и не использовал повторно объект primarySmbFile при создании inputStream
После добавления синхронизации теперь все работает нормально. Я ценю вашу помощь. Большое спасибо за вашу постоянную поддержку.
Приятно слышать, если ответ решит вашу проблему, примите его
На самом деле я поднял этот вопрос, как указано в заголовке Загрузка файла в основное место и одновременное чтение и запись одного и того же файла в несколько дополнительных мест. Это проблема с одновременным чтением и записью в основной файл. И мы попробовали другой подход: сначала полностью загрузить на первичный, а затем скопировать его во вторичные места, что является последовательным подходом. Поэтому я думаю, что ваш ответ дает мне другой подход, но не связан с заданным вопросом.
Я ценю вашу помощь. Но было бы хорошо, если бы вы могли упомянуть здесь, что в то же время чтение / запись невозможно, и я должен следовать последовательному подходу (например, первый основной файл полностью загружен, затем только чтение из основного и запись в несколько дополнительных файлов). Что ты говоришь?
это очень медленно .. можем ли мы сделать быстрее? Я обновил размер буфера, но он все еще медленный
Попробуйте этот stackoverflow.com/questions/10533653/…. В частности, свойства jcifs.resolveOrder
и jcifs.smb.client.dfs.disabled
. Программно вы можете использовать такие, как этот jcifs.Config.setProperty("jcifs.resolveOrder", "DNS");
Вы пытались рассматривать свои второстепенные местоположения как основные? Я имею в виду обернуть SmbFileOutputStream в BufferedOutputStream для secLocation1, secLocation2 и secLocation3 и не забыть сбросить